The Basics
formals()
, body()
, and environment()
are NULLsum()
, [
typeof(sum)#> [1] "builtin"typeof(`[`)#> [1] "special"
The Basics
function
and bind it to a name with <-
lapply(mtcars, function(x) length(unique(x)))Filter(function(x) !is.numeric(x), mtcars)integrate(function(x) sin(x) ^ 2, 0, pi)
The Basics
Use do.call
if arguments are already in a data structure
args <- list(1:10, na.rm = TRUE)do.call(mean, args)> [1] 5.5
The Basics
x <- runif(100)sqrt(mean(square(deviation(x))))#> [1] 0.274
✓ Concise, well suited for short sequences
x Can be hard to read
Function composition
Note- code highlight differs if r is not within parentheses Function composition
out <- deviation(x)out <- square(out)out <- mean(out)out <- sqrt(out)out#> [1] 0.274
x intermediate names required
✓ This is useful only when intermediate objects are important
Function composition
x %>% deviation() %>% square() %>% mean() %>% sqrt()#> [1] 0.274
✓ More readable code
x Requires third party package
Function composition
"It looks up the values of names based on how a function is defined, not how it is called"
Lexical scoping
Names defined within the function mask names outside the function
x <- 10y <- 20g02 <- function() { x <- 1 y <- 2 c(x, y)}g02()#> [1] 1 2
Lexical scoping
Names defined within the function mask names outside the function
x <- 10y <- 20g02 <- function() { x <- 1 y <- 2 c(x, y)}g02()#> [1] 1 2
If a name isn’t defined inside a function, R looks one level up.
x <- 2g03 <- function() { y <- 1 c(x, y)}g03()#> [1] 2 1y#> [1] 20
Lexical scoping
Names defined within the function mask names outside the function
x <- 10y <- 20g02 <- function() { x <- 1 y <- 2 c(x, y)}g02()#> [1] 1 2
If a name isn’t defined inside a function, R looks one level up.
x <- 2g03 <- function() { y <- 1 c(x, y)}g03()#> [1] 2 1y#> [1] 20
The same rules apply if a function is defined inside another function.
g09 <- function(x) x + 100g10 <- function() { g09 <- 10 g09(g09)}g10()#> [1] 110
!!! Using same name for different things is a bad idea!!!
Lexical scoping
g09 takes two different values best to not use same names (caution from Hadley) but knowing this can help us troubleshoot
g11 <- function() { if (!exists("a")) { a <- 1 } else { a <- a + 1 } a}g11()g11()
Lexical scoping
Lexical scoping
g12 <- function() x + 1x <- 15g12()#> [1] 16x <- 20g12()#> [1] 21
Lexical scoping
g12 <- function() x + 1x <- 15g12()#> [1] 16x <- 20g12()#> [1] 21
codetools
to detect the external dependenciesLexical scoping
g12 <- function() x + 1x <- 15g12()#> [1] 16x <- 20g12()#> [1] 21
codetools
to detect the external dependenciescodetools::findGlobals(g12)#> [1] "+" "x"
In R, function arguments are lazily evaluated: they’re only evaluated if accessed.
h01 <- function(x) { 10}h01(stop("This is an error!"))#> [1] 10
A promise has three components
x + y
, which gives rise to the delayed computationdouble <- function(x) { message("Calculating...") x * 2}h03 <- function(x) { c(x, x)}h03(double(20))> Calculating...> [1] 40 40
Lazy evaluation
Default arguments can be defined in terms of
Lazy evaluation
you can use missing()
to see if the argument's value comes from the user or default
h06 <- function(x = 10) { list(missing(x), x)}str(h06())> List of 2> $ : logi TRUE> $ : num 10str(h06(10))#> List of 2#> $ : logi FALSE#> $ : num 10
Lazy evaluation
First example uses default and returns TRUE second example uses user supplied so logi is FALSE
red.plot <- function(x, y, ...) { plot(x, y, col="red", ...)}red.plot(1:10, 1:10, xlab="My x axis", ylab="My y axis")
Lazy evaluation. Example from https://nicercode.github.io/guides/functions/
functions exit in two ways, they either return a value, indicating success, or they throw an error
j01 <- function(x) { if (x < 10) { 0 } else { 10 }}j01(5)#> [1] 0j01(15)#> [1] 10
j02 <- function(x) { if (x < 10) { return(0) } else { return(10) }}
Exiting a function
<-
invisible()
to the last valuej04 <- function() invisible(1)j04()
print()
or wrapping in ()
print(j04())#> [1] 1(j04())#> [1] 1
Exiting a function
If a function cannot complete its assigned task, it should throw an error with stop()
indicating the termination of function execution
j05 <- function() { stop("I'm an error") return(10)}j05()#> Error in j05(): I'm an error
Exiting a function
Undo any changes made to the global environment
j06 <- function(x) { cat("Hello\n") on.exit(cat("Goodbye!\n"), add = TRUE) if (x) { return(10) } else { stop("Error") }}j06(TRUE)> Hello> Goodbye!> [1] 10j06(FALSE)> Hello> Error in j06(FALSE): Error> Goodbye!
Exiting a function
can use it to switch back directories on exit
Any function can be written in prefix form
Function name comes before the arguments eg. foofy(a, b, c)
✓ Helps better understand the structure of the language
x + y`+`(x, y)names(df) <- c("x", "y", "z")`names<-`(df, c("x", "y", "z")) for(i in 1:10) print(i)`for`(i, 1:10, print(i))
Function forms
Function forms
Use positional matching only for the first one or two arguments; they will be the most commonly used, and most readers will know what they are.
x + y
%
`%+%` <- function(a, b) paste0(a, b)"new " %+% "string"#> [1] "new string"
%
Function forms
names(df) <- c("a", "b", "c")
xxx<-
, must have arguments named x
and value, and must return the modified object`second<-` <- function(x, value) { x[2] <- value x}
Function forms
[[
, if
, and for
`for`> .Primitive("for")
Function forms
kmeena@uw.edu, twitter: @envhealthspeak
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |