source("R/function_syntax.R")
result <- multiply(6, 7) # [function call][1] "hello"
result[1] 42
ls()[1] "multiply" "result"
https://github.com/RUMgroup/R-Functions/
ResearchIT, University of Manchester
2023-11-28
Functions are blocks of code that process input arguments and return some output.
They’re different from scripts in that they have their own environment, and clearly defined inputs and output.
[1] "hello"
[1] 42
[1] "multiply" "result"
Note
a, b, and answer exist only inside multiply)After years of research, you’ve come up with the following script:
R/bread_script.R
R/worst_idea.R
# How NOT to write variations of the same code
flour_weight <- 1.0
water_weight <- 0.7 * flour_weight
bread_weight <- 0.9 * (flour_weight + water_weight)
cat("Bread weight:", bread_weight, "kg", "\n")
flour_weight_2 <- 2.0
water_weight_2 <- 0.8 * flour_weight_2
bread_weight_2 <- 0.9 * (flour_weight_2 + water_weight_2)
cat("Bread weight:", bread_weight_2, "kg", "\n")[1] "bread_weight" "bread_weight_2" "flour_weight" "flour_weight_2"
[5] "water_weight" "water_weight_2"
R/parametric_script.R
R/still_bad.R
# Calls R/parametric_script.R with different parameters
rm(list = ls()) # clear the workspace
flour_weight <- 1.0
hydration <- 0.7
water_loss <- 0.1
source("R/parametric_script.R")
bread_weight_1 <- bread_weight
flour_weight <- 2.0
hydration <- 0.8
source("R/parametric_script.R")
bread_weight_2 <- bread_weightR/bread_function.R
R/main.R
# Calls R/parametric_script.R with different parameters
rm(list = ls()) # clear the workspace
# loads get_bread_weight function
source("R/bread_function.R")
bread_weight_1 <- get_bread_weight(flour_weight = 1.0, hydration = 0.7)
bread_weight_2 <- get_bread_weight(flour_weight = 2.0, hydration = 0.8)return is optional# without explicit `return`, the last expression is returned
multiply <- function(a, b) {
a * b
}
multiply(6, 7)[1] 42
# (this can easily lead to errors)
surprise <- function(a, b) {
result <- a * b
cat("The answer is", result, "\n")
}
answer <- surprise(6, 7)The answer is 42
NULL
# better...
no_surprise <- function(a, b) {
result <- a * b
cat("The answer is", result, "\n")
return(result)
}
answer <- no_surprise(6, 7)The answer is 42
[1] 42
# use a named list for multiple return values
rectangle <- function(a, b) {
area <- a * b
perimeter <- 2 * (a + b)
list(area = area, perimeter = perimeter)
}
result = rectangle(2, 3)
result$area[1] 6
[1] 10
See also the zeallot package, and base::list2env.
rm(list = ls())
bad_example <- function(x = 1) {
cat("variables inside:", ls(), "\n")
# search in parent environments (AVOID!)
cat("outside_c = ", outside_c, "\n")
# assignment in parent environment (AVOID!)
outside_d <<- 42
}
outside_c <- 2
outside_d <- 3
bad_example()variables inside: x
outside_c = 2
[1] 42
Describe what the function does, each of the arguments, and the return value.
You might want to use roxygen2 style comments.
R/bread_function.R
#' Calculates bread weight
#'
#' @param flour_weight Weight of flour in kg
#' @param hydration Water content per unit flour [0, 1]
#' @param water_loss Water loss fraction during baking [0, 1]
#' @return Weight of baked bread in kg
#' @export
get_bread_weight <- function(flour_weight,
hydration = 0.7,
water_loss = 0.1) {
# ...
}Reusability, Parametrization, Readability, Maintainability, Encapsulation, …
Modularity, Collaboration, Testing, Debugging (base::debugonce).
RUM space on CADIR (MS Teams): https://bit.ly/RUserGroup