--- title: "Breaking Functions" author: "JJB + Course" output: html_document: toc: true toc_float: collapsed: false --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) ``` # Ubiquitousness of Functions ## Example Recall: Add ```{r sample-function} add = function(x, y) { return(x + y) } add(1, 3) ``` ## Functions as Objects ```{r function-as-object} square = function(x) { # Create a square function x^2 } class(square) # Find high-level class information typeof(square) # Obtain low-level class information ``` ## Extracting Function Properties ```{r breaking-apart-function} formals(square) # Retrieve parameters & default values body(square) # Retrieve the function body environment(square) # Retrieve the location of function ``` ## Hidden Function Calls ```{r hidden-calls} # Addition 10 + 25 `+`(10, 25) `-`(5, 10) # Assignment x = c(1, 2, 3) `=`(x, c(1, 2, 3)) # Subset x[[1]] `[[`(x, 1) ``` ## Anonymous Functions ```{r anon-function} function(x = 4) x + 1 (function(x = 4) x + 1)(2) # Anonymous definition add_one = function(x = 4) x + 1 # Named function add_one(2) ``` ## Function as a Parameter ```{r function-as-object-param} add = function(x, y) { x + y } subtract = function(x, y) { x - y } multiply = function(x, y) { x * y } ops = function(f, x, y) { f(x , y) } ops(add, 2, 5) ops(subtract, 2, 5) ``` ### Exercise: Determine the function properties of `mean()` ```{r viewing-mean} # The parameters of the function # Look at the definition of the function # Definition is the body statements # This is the traditional definition # but, it uses an S3 class to handle different kinds # of data types. # # This requires more specification of what is happening. # Pull up help docs # ?mean ``` # Asserting Input ## Example: Maligned input ```{r naive-logit-imp} logit = function(p) { log(p/(1 - p)) } logit(.5) logit(.75) logit(1.5) logit(-.5) ``` This will trigger an error during the kniting. Why does it trigger an error? ```{r char-input-issue, error = TRUE} logit("a") ``` ## Example: Protected Input ```{r logit-safe-implementation} logit_safe = function(p) { if(!is.numeric(p)) { stop("`p` must be numeric", call. = FALSE) } else if (any(p >= 1 )) { stop("All `p` values must be less than 1", call. = FALSE) } else if (any(p <= 0)) { stop("All `p` values must be greater than 0", call. = FALSE) } log(p/(1 - p)) } logit_safe(.5) logit_safe(.75) ``` By specifying `error = TRUE` in the code chunk option, the "knit" to HTML will _not_ fail as the error state will be _saved_. ```{r logit-safe-error, error = TRUE} logit_safe(1.5) logit_safe(-.5) logit_safe("a") ``` Though, an alternative is to disable the code chunk via the traditional `eval = FALSE` option. ```{r logit-safe-eval, eval = FALSE} logit_safe(1.5) logit_safe(-.5) logit_safe("a") ``` ## Exercise: Z-score 1. Identify what is wrong with the following function 2. Propose a fix for it ```{r bad-z-implementation} z_score = function(x, mu, std) { (x - mu) / std } ``` # Debugging Errors ## Example: Error Nested Function ```{r perform-computation, error = TRUE} f = function(x) { g(x + 2) } g = function(x) { h(x - 2) } h = function(x) { val = log(x) if(val < 0) { return(val^2) } val } f(-4) ``` ```{r show-traceback} # if RStudio is not available traceback() ``` Now, we can understand the error by manually re-creating the logic: ```{r calculation-woes, error = TRUE} -4 + 2 + # this is done in f(x) -2 # done in g(x) # Retrieve NaN: Not a Number log(-4) ``` If we allow this to propogate into the _if_ statement, it becomes problematic. ```{r bad-if, error = TRUE} # if( ) { message("TRUE STATEMENT ")} else { message("FALSE STATEMENT")} if( log(-4) > 0 ) { message("TRUE!")} if(NA) { message("Did it work?")} # Complex numbers # ((1 + 5i) + (2 - 2i)) > 3 #@ Re(1+5i) ``` ## Example: Print Trace Statements Added ```{r add-print-trace, error = TRUE} f = function(x) { message("value of x in f(): ", x) # Added message g(x + 2) } g = function(x) { message("value of x in g(): ",x) # Added message h(x - 2) } h = function(x) { message("value of x in h(): ", x) # Added message val = log(x) message("value of val in h(): ",val) # Added message if(val < 0) { return(val^2) } val } f(-4) ``` ## Example: Continued... For additional examples, please see the `lec27-*.R` files as some features require direct use of _R_ Scripts instead of RMarkdown.