</>
Skip to content
R lessons (16/41)

R — Functions

Defining functions

Functions in R use the function keyword:

greet <- function(name) {
  paste("Hello,", name, "!")
}

greet("Ada")  # "Hello, Ada !"

Functions are assigned to variables like any other object.

Parameters and arguments

add <- function(a, b) {
  return(a + b)
}

add(3, 7)  # 10

Parameters are listed in parentheses. R matches arguments by position or name.

Named arguments

greet <- function(name, greeting = "Hello") {
  paste(greeting, name, "!")
}

greet("Ada")                    # "Hello Ada !"
greet("Ada", greeting = "Hey")  # "Hey Ada !"
greet(greeting = "Hi", name = "Ada")  # "Hi Ada !"

Default values make parameters optional. Named arguments can go in any order.

Return values

# Explicit return
add <- function(a, b) {
  return(a + b)
}

# Implicit return — last expression
add <- function(a, b) {
  a + b  # no return() needed
}

Both work. Use return() for early exits; let the last expression be the implicit return.

Multiple return values

stats <- function(x) {
  list(
    mean = mean(x),
    sd = sd(x),
    n = length(x)
  )
}

result <- stats(rnorm(100))
result$mean  # average
result$sd    # standard deviation

Return a list when you need multiple outputs.

... (dot-dot-dot) — variadic arguments

multi_sum <- function(...) {
  args <- list(...)
  sum(unlist(args))
}

multi_sum(1, 2, 3)        # 6
multi_sum(1, 2, 3, 4, 5)  # 15

... collects any number of arguments into a list.

Passing functions as arguments

apply_to_each <- function(f, x) {
  result <- c()
  for (item in x) {
    result <- c(result, f(item))
  }
  result
}

apply_to_each(function(x) x^2, 1:5)  # 1 4 9 16 25
apply_to_each(function(x) x + 10, 1:5)  # 11 12 13 14 15

Functions are first-class objects in R — you can pass them around like any other value.

Anonymous functions

# Inline function
sapply(1:5, function(x) x^2)  # 1 4 9 16 25

# Lambda-style (R 4.1+)
sapply(1:5, \(x) x^2)  # 1 4 9 16 25

Anonymous functions are useful when you need a short function temporarily.

Vectorized functions

Write functions that work on vectors automatically:

celsius_to_fahrenheit <- function(celsius) {
  celsius * 9/5 + 32
}

celsius_to_fahrenheit(c(0, 20, 37, 100))
# 32 68 98.6 212

No loop needed — the function works on each element because R is vectorized.

Higher-order functions

Functions that operate on other functions:

# sapply — apply function, simplify result
sapply(1:5, function(x) x^2)
# 1 4 9 16 25

# lapply — apply function, return list
lapply(1:5, function(x) x^2)
# list(1, 4, 9, 16, 25)

# apply — apply function to rows/columns of a matrix
m <- matrix(1:12, nrow = 3)
apply(m, 1, sum)   # row sums
apply(m, 2, mean)  # column means

# tapply — apply function by group
tapply(mtcars$mpg, mtcars$cyl, mean)

# Reduce — collapse to single value
reduce(1:5, function(a, b) a + b)  # 15

Scope and closures

make_counter <- function() {
  count <- 0
  function() {
    count <<- count + 1
    count
  }
}

counter <- make_counter()
counter()  # 1
counter()  # 2
counter()  # 3

The inner function "remembers" count from its enclosing scope. <<- modifies the enclosing environment's variable.

Documenting functions

calculate_bmi <- function(weight, height) {
  #' Calculate Body Mass Index (BMI)
  #'
  #' @param weight Weight in kilograms
  #' @param height Height in meters
  #' @return BMI as a numeric value
  #' @examples
  #' calculate_bmi(70, 1.75)
  weight / (height^2)
}

Roxygen comments (#') generate documentation with devtools::document().

Error handling

safe_divide <- function(a, b) {
  if (b == 0) {
    stop("Cannot divide by zero")
  }
  a / b
}

tryCatch(
  safe_divide(10, 0),
  error = function(e) {
    cat("Error:", e$message, "\n")
  }
)

stop() raises an error. tryCatch() catches errors gracefully.

Recursion

factorial <- function(n) {
  if (n <= 1) return(1)
  n * factorial(n - 1)
}

factorial(5)  # 120

fibonacci <- function(n) {
  if (n <= 1) return(n)
  fibonacci(n - 1) + fibonacci(n - 2)
}

sapply(0:10, fibonacci)  # 0 1 1 2 3 5 8 13 21 34 55

R supports recursion but doesn't optimize tail calls. For deep recursion, use iterative approaches.

Mini Practice

  1. Write a function that takes a vector and returns its mean, median, and standard deviation as a list
  2. Create a function with default parameters for a personalized greeting
  3. Write a vectorized function that converts temperatures from Celsius to Fahrenheit
  4. Use sapply() to apply your function to a sequence of values
  5. Write a recursive function that calculates the nth Fibonacci number

Next: conditionals — if, else, and switch →

Related Topics

Frequently Asked Questions about Functions

What is Functions in R?

Functions is a fundamental concept in R. This lesson explains it step by step with clear examples, making it easy for beginners to understand.

How do I learn Functions?

Start by reading the explanation above, then try the code examples. Practice by modifying the examples and experimenting with different values. Hands-on practice is the best way to learn Functions.

Why is Functions important in R?

Functions is essential for R development. Understanding this concept will help you write better code and solve real-world problems more effectively.