class: title-slide, middle, inverse .leftcol30[ <center> <img src="images/University-of-Maine-Logo.png" width=250> </center> ] .rightcol70[ # Week 3: .fancy[Creating Functions] ### STS 434 Tuesday R lab ### Zheng Wei ] --- --- class: center --- class: inverse, middle # Week 3: .fancy[Creating Functions] ## 1. Function syntax ## 2. Local vs global variables ## 3. Top-down design ## 4. Coding style --- class: inverse, middle # Week 3: .fancy[Creating Functions] ## 1. .orange[Function syntax] ## 2. Local vs global variables ## 3. Top-down design ## 4. Coding style --- # Basic function syntax .code90[ ```r functionName <- function(arguments) { # Do stuff here return(something) } ``` ] --- # Basic function syntax In English: > "`functionName` is a `function` of `arguments` that does..." .code90[ ```r functionName <- function(arguments) { # Do stuff here return(something) } ``` ] --- # Basic function syntax Example: > "`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) } ``` `cat()` _prints_ a value as a string ```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 # Code tracing practice .leftcol[.code80[ Consider these functions: ```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[ 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] ## 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 <- 5 # Define n in the *global* environment print_x(5) ``` ``` ## 55 ``` ] -- .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 <- 5 # 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 ## Code tracing practice .leftcol[.code70[ Consider this code: ```r x <- 7 y <- NULL 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[ 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, center --- class: inverse, middle # Week 3: .fancy[Creating Functions] ## 1. Function syntax ## 2. Local vs global variables ## 3. .orange[Top-down design] ## 4. Coding style --- # "Top Down" design <br> -- ## 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 hypotenuse) <br> .noborder[<img src="images/right-triangle-hypotenuse.png" width="250">] <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 hypotenuse) <br> .noborder[<img src="images/right-triangle-hypotenuse.png" width="250">] ] .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 hypotenuse) <br> .noborder[<img src="images/right-triangle-hypotenuse.png" width="250">] ] .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 # Think-Pair-Share 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 in your solution. **Hint**: you may not know which value (`a`, `b`, or `c`) is the hypotenuse. .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 ## 3. Top-down design ## 4. .orange[Coding style] ] .rightcol40[ <img src="images/code_style.jpg" width="370px"> ] --- # Style matters! -- ## Which is easier to read? .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 read? .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) } ``` ]] -- ## 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`) --- # Style guide: **Functions** Generally, function names should be verbs: ```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 .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 # Think-Pair-Share .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 ## Think-Pair-Share .leftcol[ `eggCartons(eggs)`: Write a function that reads in a non-negative number of eggs and prints 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[ 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 ]