class: middle, inverse .leftcol30[ <center> <img src="https://github.com/emse-p4a-gwu/emse-p4a-gwu.github.io/blob/master/images/logo.png?raw=true" width=250> </center> ] .rightcol70[ # Week 3: .fancy[Creating Functions] ###
EMSE 4571 / 6571: Intro to Programming for Analytics ###
John Paul Helveston ###
February 01, 2024 ] --- class: inverse # Quiz 2
10
:
00
.leftcol[ ## Write your name on the quiz! ## Rules: - Work alone; no outside help of any kind is allowed. - No calculators, no notes, no books, no computers, no phones. ] .rightcol[ <br> <center> <img src="https://github.com/emse-p4a-gwu/2022-Spring/raw/main/images/quiz_doge.png" width="400"> </center> ] --- class: inverse, middle # Week 3: .fancy[Creating Functions] ### 1. Function syntax ### 2. Local vs global variables ### BREAK ### 3. Top-down design ### 4. Coding style --- class: inverse, middle # Week 3: .fancy[Creating Functions] ### 1. .orange[Function syntax] ### 2. Local vs global variables ### BREAK ### 3. Top-down design ### 4. Coding style --- # Basic function syntax .code90[ ```r name <- function(arguments) { # Do stuff here return(something) } ``` ] --- # Basic function syntax In English: > "`name()` is a `function` of `arguments` that does..." In Code: .code90[ ```r name <- function(arguments) {} ``` ] --- # Basic function syntax > "`squareRoot()` is a `function` of `n` that...returns the square root of `n`" .code90[ ```r squareRoot <- function(n) { return(n^0.5) } ``` ] -- .code90[ ```r squareRoot(64) ``` ``` #> [1] 8 ``` ] --- # `return()` and `cat()` statements -- .leftcol[.code90[ ```r isPositive <- function(n) { return(n > 0) } ``` ]] -- .rightcol[.code90[ ```r isPositive <- function(n) { cat(n > 0) } ``` ]] --- # `return()` and `cat()` statements .leftcol[.code90[ ```r isPositive <- function(n) { return(n > 0) } ``` `return()` _returns_ back a value ```r test <- isPositive(7) test ``` ``` TRUE ``` ]] .rightcol[.code90[ ```r isPositive <- function(n) { cat(n > 0) } ``` ]] --- # `return()` and `cat()` statements .leftcol[.code90[ ```r isPositive <- function(n) { return(n > 0) } ``` `return()` _returns_ back a value ```r test <- isPositive(7) test ``` ``` TRUE ``` ]] .rightcol[.code90[ ```r isPositive <- function(n) { cat(n > 0) } ``` `cat()` _prints_ a value to the console ```r test <- isPositive(7) ``` ``` TRUE ``` ```r test ``` ``` Error: object 'test' not found ``` ]] --- ## `cat()` is short for "concatenating" -- ```r print_x <- function(x) { cat("The value of x is", x) } ``` -- ```r print_x(7) ``` ``` #> The value of x is 7 ``` -- ```r print_x_squared <- function(x) { cat("The value of x is", x, "and the value of x^2 is", x^2) } ``` -- ```r print_x_squared(7) ``` ``` #> The value of x is 7 and the value of x^2 is 49 ``` --- ## `cat()` adds a space between values by default -- ```r print_x <- function(x) { cat("The value of x is", x) } ``` -- ```r print_x(7) ``` ``` #> The value of x is 7 ``` -- Modify separator with the `sep` argument: ```r print_x <- function(x) { cat("The value of x is", x, sep = ": ") } ``` -- ```r print_x(7) ``` ``` #> The value of x is: 7 ``` --- class: inverse
05
:
00
# Your turn: Code tracing practice .leftcol[.code80[ ```r f1 <- function(x) { return(x^3) } f2 <- function(x) { cat(x^3) } f3 <- function(x) { cat(x^3) return(x^4) } f4 <- function(x) { return(x^3) cat(x^4) } ``` ]] .rightcol[.code80[ Considering the functions on the left, what will these lines of code produce? Write your answer down first, _then_ run the code to check. ```r f1(2) f2(2) f3(2) f4(2) ``` ]] --- class: inverse, middle # Week 3: .fancy[Creating Functions] ### 1. Function syntax ### 2. .orange[Local vs global variables] ### BREAK ### 3. Top-down design ### 4. Coding style --- # Local objects ### All objects inside function are **"local"** - they don't exist in the _global_ environment -- .leftcol[.code90[ ### Example: ```r squareOfX <- function(x) { * y <- x^2 # y here is "local" return(y) } ``` ]] -- .rightcol[ ```r squareOfX(3) ``` ``` #> [1] 9 ``` If you try to call `y`, you'll get an error: ```r y ``` ``` Error: object 'y' not found ``` ] --- # Global objects ### **Global** objects exist in the main environment. -- ### **NEVER, NEVER, NEVER** call global objects inside functions. -- .leftcol[ ```r print_x <- function(x) { cat(x) * cat(n) # n is global! } n <- 7 # Define n in the *global* environment print_x(5) ``` ``` #> 57 ``` ] -- .rightcol[ ```r n <- 6 print_x(5) ``` ``` #> 56 ``` **Function behavior shouldn't change with the same arguments!** ] --- # Global objects ### All objects inside functions should be **arguments** to that function -- .leftcol[ ```r print_x <- function(x, n = NULL) { cat(x) * cat(n) # n is local! } n <- 7 # Define n in the *global* environment print_x(5) ``` ``` #> 5 ``` ] -- .rightcol[ ```r n <- 6 print_x(5) ``` ``` #> 5 ``` Use `n` as argument: ```r print_x(5, n) ``` ``` #> 56 ``` ] --- class: inverse
10
:
00
# Your turn: Code tracing practice .leftcol[.code70[ ```r f1 <- function(x) { cat(x^3) cat(y, x) } f2 <- function(x, y = 7) { cat(x^3, y) } f3 <- function(x, y) { cat(x^3) cat(y) } f4 <- function(x) { return(x^3) cat(x^4) } ``` ]] .rightcol[.code70[ Considering the functions on the left, what will these lines of code produce? Write your answer down first, _then_ run the code to check. ```r x <- 7 y <- NULL f1(2) f2(2) f3(2) f4(2) ``` ]] --- class: inverse, center # .fancy[Intermission]
05
:
00
--- class: inverse, middle # Week 3: .fancy[Creating Functions] ### 1. Function syntax ### 2. Local vs global variables ### BREAK ### 3. .orange[Top-down design] ### 4. Coding style --- # "Top Down" design -- ## 1. Break the problem into pieces -- ## 2. Solve the "highest level" problem first -- ## 3. Then solve the smaller pieces --- .leftcol40[ **Example**: Given values `a` and `b`, find the value `c` such that the triangle formed by lines of length `a`, `b`, and `c` is a right triangle (in short, find the hypotenuse) <br> <img src="images/right-triangle-hypotenuse.png" width=100%> <br> ] --- .leftcol40[ **Example**: Given values `a` and `b`, find the value `c` such that the triangle formed by lines of length `a`, `b`, and `c` is a right triangle (in short, find the hypotenuse) <br> <img src="images/right-triangle-hypotenuse.png" width=100%> ] .rightcol55[ ### Hypotenuse: `\(c = \sqrt{a^2 + b^2}\)` ### Break the problem into two pieces: ### `\(c = \sqrt{x}\)` ### `\(x = a^2 + b^2\)` ] --- .leftcol40[ **Example**: Given values `a` and `b`, find the value `c` such that the triangle formed by lines of length `a`, `b`, and `c` is a right triangle (in short, find the hypotenuse) <br> <img src="images/right-triangle-hypotenuse.png" width=100%> ] .rightcol55[ ### Hypotenuse: `\(c = \sqrt{a^2 + b^2}\)` ### Break the problem into two pieces: ### `\(c = \sqrt{x}\)` ```r hypotenuse <- function(a, b) { return(sqrt(sumOfSquares(a, b))) } ``` ### `\(x = a^b + b^2\)` ```r sumOfSquares <- function(a, b) { return(a^2 + b^2) } ``` ] --- class: inverse
12
:
00
# Your turn Create a function, `isRightTriangle(a, b, c)` that returns `TRUE` if the triangle formed by the lines of length `a`, `b`, and `c` is a right triangle and `FALSE` otherwise. Use the `hypotenuse(a, b)` function below in your solution. .leftcol[.code80[ ```r hypotenuse <- function(a, b) { return(sqrt(sumOfSquares(a, b))) } ``` ```r sumOfSquares <- function(a, b) { return(a^2 + b^2) } ``` ]] --- class: inverse, middle .leftcol60[ # Week 3: .fancy[Creating Functions] ### 1. Function syntax ### 2. Local vs global variables ### BREAK ### 3. Top-down design ### 4. .orange[Coding style] ] .rightcol40[ <img src="images/code_style.jpg" width="370px"> ] --- # Style matters! -- ## Which is easier to understand? .leftcol60[.code80[ V1: ```r sumofsquares<-function(a,b)return(a^2 + b^2) ``` V2: ```r sum_of_squares <- function(a, b) { return(a^2 + b^2) } ``` ]] --- # Style matters! ## Which is easier to understand? .leftcol60[.code80[ V1: ```r sumofsquares<-function(a,b)return(a^2 + b^2) ``` V2: <- **This one is _much_ better!** ```r sum_of_squares <- function(a, b) { return(a^2 + b^2) } ``` ]] --- # Use the "Advanced R" style guide: ## http://adv-r.had.co.nz/Style.html <br> -- ## Other good style tips on [this blog post](https://www.r-bloggers.com/%F0%9F%96%8A-r-coding-style-guide/) --- # Style guide: **Objects** .leftcol[ <img src="images/assignment.jpg" width="500px"> ] -- .rightcol[ - Use ` <- ` for assignment, not ` = ` - Put spacing around operators<br>(e.g. `x <- 1`, not `x<-1`) - Use [meaningful variable names](https://p4a.jhelvy.com/getting-started.html#use-meaningful-variable-names) - This applies to file names too<br>(e.g. "`hw1.R`" vs. "`untitled.R`") ] --- # Style guide: **Functions** Generally, function names should be **verbs** because they **do things**: ```r add() # Good addition() # Bad ``` -- Avoid using the "`.`" symbol: ```r get_hypotenuse() # Good get.hypotenuse() # Bad ``` -- Use curly braces, with indented code inside: ```r sum_of_squares <- function(a, b) { return(a^2 + b^2) } ``` --- class: center Tools -> Global options .leftcol[ **Indent by 4 spaces** <center> <img src="images/indent.png" width="550px"> </center> ] .rightcol[ **Set line length to 80** <center> <img src="images/length.png" width="550px"> </center> ] --- class: inverse
15
:
00
# Your turn .leftcol[ `onesDigit(x)`: Write a function that takes an integer and returns its ones digit. Tests: - onesDigit(123) == 3 - onesDigit(7890) == 0 - onesDigit(6) == 6 - onesDigit(-54) == 4 ] .rightcol[ `tensDigit(x)`: Write a function that takes an integer and returns its tens digit. Tests: - tensDigit(456) == 5 - tensDigit(23) == 2 - tensDigit(1) == 0 - tensDigit(-7890) == 9 ] --- class: inverse ### Hint #1: You may want to use `onesDigit(x)` as a helper function for `tensDigit(x)` ### Hint #2: .leftcol[ The mod operator (`%%`) "chops" a number and returns everything to the _right_ ```r 123456 %% 1 ``` ``` #> [1] 0 ``` ```r 123456 %% 10 ``` ``` #> [1] 6 ``` ] .rightcol[ The integer divide operator (`%/%`) "chops" a number and returns everything to the _left_ ```r 123456 %/% 1 ``` ``` #> [1] 123456 ``` ```r 123456 %/% 10 ``` ``` #> [1] 12345 ``` ] --- class: inverse
15
:
00
# Your turn .leftcol[ `eggCartons(eggs)`: Write a function that takes a non-negative number of eggs and returns the number of egg cartons required to hold that many eggs. Each egg carton holds one dozen eggs, and you cannot buy fractional egg cartons. - eggCartons(0) == 0 - eggCartons(1) == 1 - eggCartons(12) == 1 - eggCartons(25) == 3 ] .rightcol[ `militaryTimeToStandardTime(n)`: Write a function that takes an integer between 0 and 23 (representing the hour in [military time](http://militarytimechart.com/)), and returns the same hour in standard time. - militaryTimeToStandardTime(0) == 12 - militaryTimeToStandardTime(3) == 3 - militaryTimeToStandardTime(12) == 12 - militaryTimeToStandardTime(13) == 1 - militaryTimeToStandardTime(23) == 11 ]