R: R Functions
Last updated: 2026-08-26
In the previous 6 lessons, we’ve been using R’s built-in functions (
sum()mean()c()). But what really makes R powerful is that you can write your own functions. In this lesson, we’ll learn about R’s “custom functions”—wrapping repetitive code into reusable tools.
R is a functional programming language, where functions are "first-class citizens." This means that functions can be assigned to variables, passed as arguments, and returned as values. In this lesson, we will explore all the features of R functions.
1. What You'll Learn
- function(){} Basic Syntax
- 4 types of parameters (positional, named, default, and variable parameters ...)
- 3 types of return values (return / last line / invisible)
- Anonymous functions (lambda)
- Functions as Objects (Assignment, Passing as Arguments, Returning)
- do.call() batch calls
- Hands-On: Write a Complete Utility Function
2. A Story About Code Reuse
(1) Pain Point: Duplicate Code
Chen is a data analyst who has to "convert sales data into reports" 10 times a day. Each time, he writes the same thing over and over:
# Repeat 10 The code for this
sales <- c(120, 150, 80, 200, 175)
total <- sum(sales)
avg <- mean(sales)
top <- max(sales)
cat("Total Sales:", total, "Average:", avg, "Highest:", top, "\n")
10 copy-and-paste operations = 200 lines of duplicate code. If you have to change the logic in one place, you have to change it in 10 places—sooner or later, you’ll miss something.
(2) Solving Functions
# Write a function (just write 1 time)
report <- function(x) {
cat("Total Sales:", sum(x), " Average:", mean(x), " Highest:", max(x), "\n")
}
# Call 10 times (Every time 1 row)
report(c(120, 150, 80, 200, 175))
report(c(80, 90, 100))
report(c(1000, 2000, 3000))
5 lines of function code + 10 lines of calls = 15 lines of code. Changing the logic requires only one modification. That’s the power of functions.
3. function(){}: Basic Syntax
(1) Function Definition
# The Simplest Function
greet <- function() {
cat("Hello, World!\n")
}
# Call
greet()
# Output:Hello, World!
(2) Functions with Parameters
# Single parameter
greet <- function(name) {
cat("Hello,", name, "!\n")
}
greet("Alice")
# Output:Hello, Alice !
# Multiple parameters
greet_full <- function(name, age) {
cat("I am", name, ",This year", age, "yrs\n")
}
greet_full("Bob", 25)
# Output:I am Bob ,This year 25 yrs
(3) Differences Between R and Python Functions
| Feature | R | Python |
|---|---|---|
| definition | function(x) { ... } |
def func(x): |
| Code Block | {} |
Indentation |
| Return Value | The last line is automatically returned | Requires return |
| Documentation | Starts with #' (roxygen2) |
docstring |
| Type declaration | None (dynamically typed) | Optional (Python 3.5+) |
# R the special:The last line wraps automatically(Not necessary return)
add <- function(a, b) {
a + b # The last line wraps automatically
}
result <- add(3, 4)
result
# [1] 7
4. 4 Types of Parameters
(1) Position Parameters vs. Named Parameters
# Function Definition:3 parameter
power <- function(base, exp, mod) {
result <- (base ^ exp) %% mod
return(result)
}
# Location Parameters(In order)
power(2, 10, 1000)
# [1] 24 ← 2^10=1024, 1024 %% 1000 = 24
# Named parameters(By Name,Out of order)
power(mod = 1000, base = 2, exp = 10)
# [1] 24 ← The same result
(2) Default Parameters
# The second parameter has a default value.
power <- function(base, exp = 2, mod = 1000) {
result <- (base ^ exp) %% mod
return(result)
}
# Do not pass a second parameter,Use the default exp = 2
power(10)
# [1] 100 ← 10^2=100, 100 %% 1000 = 100
# Partial Parameter Passing
power(10, mod = 100)
# [1] 0 ← 10^2=100, 100 %% 100 = 0
(3) Rules for Parameter Order
R's parameter matching rules:
graph TB
A[Function Call power 10 exp=3] --> B[1. Exact Match for Named Parameters]
B --> C[2. Prefix Matching]
C --> D[3. Location Matching]
style A fill:#fff3cd
style B fill:#d4edda
style C fill:#d4edda
style D fill:#d4edda
- Exact match:
exp = 3(most specific) - Prefix Matching:
e = 3→exp(fewer keystrokes) - Position Matching: First unnamed parameter →
base(least secure)
exp = 3). It doesn’t save much time to type a little more, but it helps avoid ambiguity.
(4) Variable Parameters ...
... stands for "any number of arguments" and is commonly used in wrapper functions:
# Use ... to accept any parameters
my_sum <- function(...) {
args <- list(...)
result <- sum(unlist(args))
cat("Summed up", length(args), "parameter:", result, "\n")
return(result)
}
my_sum(1, 2, 3)
# Summed up 3 parameter: 6
my_sum(10, 20, 30, 40, 50)
# Summed up 5 parameter: 150
... is very commonly used when wrapping other functions (such as when a custom my_mean() wraps some parameters of mean()).
5. 3 Types of Return Values
(1) The last line wraps automatically
The last expression in an R function automatically returns:
add <- function(a, b) {
a + b # Last line,Auto-Return
}
add(3, 4)
# [1] 7
(2) Explicit return()
# Use return() to return early
check_age <- function(age) {
if (age < 0) {
return("Age of Invalidity") # Return Early
}
if (age >= 18) {
return("Adulthood")
}
"Minor" # Implicit Return
}
check_age(-5) # [1] "Age of Invalidity"
check_age(20) # [1] "Adulthood"
check_age(15) # [1] "Minor"
(3) invisible(): Hide the return value
invisible() Prevent a function from automatically printing its return value (but still allow it to be assigned):
# Ordinary Functions
add_print <- function(a, b) {
a + b # It will print automatically
}
add_print(3, 4)
# [1] 7 ← Automatic Printing
# invisible Function
add_silent <- function(a, b) {
invisible(a + b) # Do not print automatically
}
add_silent(3, 4)
# No output ← Do not print
# But it can still be assigned a value
result <- add_silent(3, 4)
result
# [1] 7 ← You can see it after the assignment.
<- Assigning a value using invisible() — x <- 5 does not print the result.
6. Anonymous Functions: Lambda Expressions
R supports anonymous functions (functions without names):
# Ordinary Functions
square <- function(x) x ^ 2
square(5)
# [1] 25
# Anonymous Functions(Use directly)
(function(x) x ^ 2)(5)
# [1] 25
# Common Use Cases for Anonymous Functions:lapply/sapply
numbers <- list(1, 2, 3, 4, 5)
result <- lapply(numbers, function(x) x ^ 2)
result
# [[1]] 1
# [[2]] 4
# [[3]] 9
# [[4]] 16
# [[5]] 25
apply family of functions—we’ll cover this in depth in Lesson 8.
7. Functions as Objects (The Core of Functional Programming in R)
Functions in R are "first-class," which means that functions can:
(1) Assigning a value to a variable
f <- function(x) x * 2
g <- f # A function has two names
f(5) # [1] 10
g(5) # [1] 10 ← Equivalent
(2) Passed as a parameter
# Functions that take other functions as arguments
apply_twice <- function(f, x) {
f(f(x))
}
# Passing Different Functions
apply_twice(function(x) x + 1, 5) # 5+1+1=7
apply_twice(function(x) x * 2, 5) # 5*2*2=20
(3) As a return value
# A function that returns a function(Closure)
make_multiplier <- function(k) {
function(x) x * k
}
# Creating Functions with Different Scales
double <- make_multiplier(2)
triple <- make_multiplier(3)
double(5) # [1] 10
triple(5) # [1] 15
map() reduce()) make use of this.
8. do.call(): Dynamic Calling
do.call() Expand a list/vector into function arguments:
# Regular Call
sum(1, 2, 3, 4, 5)
# [1] 15
# do.call Call(The parameter is a list)
args <- list(1, 2, 3, 4, 5)
do.call(sum, args)
# [1] 15
# Practical Application:Dynamic Creation data.frame
col_names <- c("name", "age", "score")
col1 <- c("Alice", "Bob", "Charlie")
col2 <- c(25, 30, 35)
col3 <- c(95, 88, 92)
df <- do.call(data.frame, list(
name = col1,
age = col2,
score = col3
))
print(df)
# name age score
# 1 Alice 25 95
# 2 Bob 30 88
# 3 Charlie 35 92
9. Scope: Where Are Variables Valid?
R uses lexical scoping:
# Global Variables
x <- 10
# Global variables can be accessed within a function.
read_x <- function() {
cat("x =", x, "\n") # Read the whole picture x
}
read_x()
# x = 10
# However, local variables within a function do not affect the global scope.
set_local <- function() {
x <- 999 # Local variables
cat("Inside a function x =", x, "\n")
}
set_local()
# Inside a function x = 999
cat("Global x =", x, "\n")
# Global x = 10 <- Global x unchanged
global). To modify them, use <<- (but this is not recommended).
10. Complete Example: Sales Report Tool Functions
Below is an example of a complete workflow that ties together all the function features covered in this lesson.
▶ Example: Sales Report Tool Functions
# ============================================
# Sales Report Tool Functions
# Features:Encapsulate Common Analytical Logic,Reuse
# ============================================
# 1. Basic Statistical Functions(Back to List)
basic_stats <- function(x) {
list(
n = length(x),
sum = sum(x),
mean = round(mean(x), 2),
median = median(x),
sd = round(sd(x), 2),
min = min(x),
max = max(x)
)
}
# 2. Formatted Output Functions(invisible Back)
print_report <- function(name, x) {
stats <- basic_stats(x)
cat("=================================\n")
cat("Sales Report:", name, "\n")
cat("=================================\n")
cat("Sample size:", stats$n, "\n")
cat("Total Sales:", stats$sum, "\n")
cat("Average Sales:", stats$mean, "\n")
cat("Median:", stats$median, "\n")
cat("Standard Deviation:", stats$sd, "\n")
cat("Minimum:", stats$min, " / Maximum:", stats$max, "\n")
invisible(stats) # Silent Return
}
# 3. Classification Function(Vectorization)
classify_sales <- function(x) {
ifelse(x < 100, "Low",
ifelse(x < 200, "Mid", "High"))
}
# 4. Main Program
sales_data <- list(
"Beijing" = c(120, 150, 80, 200, 175),
"Shanghai" = c(95, 110, 130, 145, 160),
"Shenzhen" = c(200, 220, 180, 250, 300)
)
# Generate reports for each branch
for (city in names(sales_data)) {
sales <- sales_data[[city]]
print_report(city, sales)
# Batch Classification Using Anonymous Functions
levels <- sapply(sales, function(x) classify_sales(x))
cat("\nCategory Summary:\n")
print(table(levels))
cat("\n")
}
# 5. Use do.call to merge all branches
all_sales <- do.call(c, sales_data)
cat("=== Company-wide Summary ===\n")
print_report("Company-wide", all_sales)
Expected Output (Excerpt):
=================================
Sales Report: Beijing
=================================
Sample size: 5
Total Sales: 725
Average Sales: 145
Median: 150
Standard Deviation: 47.7
Minimum: 80 / Maximum: 200
Category Summary:
levels
Mid High
3 2
=================================
Sales Report: Shanghai
=================================
...
❓ FAQ
return()?return(). However, using return() provides greater clarity, especially when returning early from an if condition.function(a = 1, b) This is valid (default parameters first, then non-default parameters), but all named parameters must be used when calling the function....?... means "accept any number of parameters" and is often used to wrap other functions. For example, my_mean <- function(x, ...) mean(x, na.rm = TRUE, ...) wraps the additional parameters of mean().<<- to do so (not recommended), but this will pollute the global environment. Good practice is for functions to return a value only and not modify external state.lapply(), sapply(), map(), etc.📖 Summary
- R function definition
function(params) { code }, the last line is automatically returned - 4 types of parameters: positional, named (
name = value), default (name = default), and variable (...) - 3 return values: last line (automatic),
return()(explicit),invisible()(silent, no output) - Anonymous functions
function(x) x^2are extremely common inlapplyandsapply - R is a functional language, and functions are "first-class citizens": they can be assigned, passed as arguments, and returned (closures).
do.call(func, list)Expand the list into function arguments- R uses lexical scope; by default, global variables cannot be modified within functions (using
<<-, though this is not recommended)
📝 Exercises
-
Basic Problem: Write a function
celsius_to_fahrenheit(c)that converts degrees Celsius to degrees Fahrenheit (formula: F = C × 9/5 + 32). Test inputs 0, 25, and 100, and verify that the outputs are 32, 77, and 212. -
Basic Problem: Write a function
circle_area(r)that calculates the area of a circle (pi * r^2), rounding to 2 decimal places. Test with r = 1, 2, 5. -
Basic Problem: Write a function
greet(name = "World", greeting = "Hello")with default parameters that outputs "Hello, World!" or "Hi, Alice!", and test three different ways of calling it. -
Advanced Problem: Write a function
analyze_scores(scores)that takes a vector of numbers and returns a list containingmeanmedianmaxminpass_ratepass rates, usinginvisible()to prevent automatic printing. After calling the function, useresult$pass_rateto access the pass rates. -
Challenge: Write a function that returns a function
make_power(n)that returnsfunction(x) x^n. Usemake_power(2)to createsquare, usemake_power(3)to createcube, and testsquare(5) = 25andcube(3) = 27. Save a screenshot.