Intermediate
Functions
Learn to define reusable functions, use the pipe operator, handle errors with tryCatch, and understand R's scoping rules.
Defining Functions
R
# Basic function greet <- function(name) { paste("Hello,", name) } greet("Alice") # "Hello, Alice" # Function with multiple parameters add <- function(a, b) { a + b } add(3, 5) # 8
Parameters and Default Values
R
# Default parameter values power <- function(base, exponent = 2) { base ^ exponent } power(3) # 9 (uses default exponent=2) power(3, 3) # 27 # Named arguments (any order) power(exponent = 3, base = 2) # 8 # Variable arguments with ... my_sum <- function(...) { args <- c(...) sum(args) } my_sum(1, 2, 3, 4) # 10
Return Values
In R, a function returns the last evaluated expression. You can also use return() explicitly:
R
# Implicit return (last expression) square <- function(x) { x ^ 2 } # Explicit return safe_divide <- function(a, b) { if (b == 0) { return(NA) } a / b } # Return multiple values with a list stats <- function(x) { list( mean = mean(x), sd = sd(x), n = length(x) ) } result <- stats(c(1, 2, 3, 4, 5)) result$mean # 3 result$sd # 1.581139
Anonymous Functions
R
# Traditional anonymous function sapply(1:5, function(x) x^2) # R 4.1+ shorthand: \(x) replaces function(x) sapply(1:5, \(x) x^2)
Scope: Local and Global
R
x <- 10 # Global variable my_func <- function() { x <- 20 # Local variable (shadows global) print(x) # 20 } my_func() print(x) # 10 (global unchanged) # Use <<- to modify global variable (use sparingly!) counter <- 0 increment <- function() { counter <<- counter + 1 } increment() print(counter) # 1
Built-in Functions
R has thousands of built-in functions. Here are some commonly used ones:
R
# Math sum(1:10) # 55 mean(c(2,4,6)) # 4 max(c(3,1,4,1,5)) # 5 min(c(3,1,4,1,5)) # 1 range(1:10) # 1 10 # String nchar("hello") # 5 grep("a", c("apple","banana","cherry")) # 1 2 # Type checking is.na(NA) # TRUE is.null(NULL) # TRUE # Getting help ?mean # Open help page for mean help(sum) # Same as ?sum
The Pipe Operator
The pipe passes the result of one function as the first argument to the next. R has two pipe operators:
R
# magrittr pipe (tidyverse) - available after library(magrittr) or library(dplyr) library(dplyr) c(1, 2, 3, 4, 5) %>% mean() %>% round(2) # 3 # Native pipe (R 4.1+) - no package needed c(1, 2, 3, 4, 5) |> mean() |> round(2) # 3 # Without pipe (nested, harder to read) round(mean(c(1, 2, 3, 4, 5)), 2)
Error Handling with tryCatch
R
# tryCatch - R's try/except equivalent safe_log <- function(x) { tryCatch( { log(x) }, warning = function(w) { message("Warning: ", w$message) NA }, error = function(e) { message("Error: ", e$message) NA } ) } safe_log(10) # 2.302585 safe_log(-1) # Warning: NaN produced, returns NA safe_log("abc") # Error: non-numeric argument, returns NA # Simple try (less control) result <- try(log("text"), silent = TRUE) if (inherits(result, "try-error")) { print("An error occurred") } # stop() to raise errors, warning() for warnings validate_age <- function(age) { if (age < 0) stop("Age cannot be negative") if (age > 150) warning("Age seems unrealistic") age }
Function Documentation
R
#' Calculate Body Mass Index (BMI) #' #' @param weight Weight in kilograms #' @param height Height in meters #' @return BMI value as a numeric #' @examples #' calculate_bmi(70, 1.75) calculate_bmi <- function(weight, height) { if (height <= 0) stop("Height must be positive") weight / height^2 }
Tip: The
#' comments use roxygen2 syntax, the standard for documenting R functions in packages. RStudio can generate these templates automatically.