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.