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



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:

R
# 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

R
# 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

R
# The Simplest Function
greet <- function() {
  cat("Hello, World!\n")
}

# Call
greet()
# Output:Hello, World!

(2) Functions with Parameters

R
# 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
# 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

R
# 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
💡 Tip: When there are multiple parameters, it's recommended to use named parameters for better readability.

(2) Default Parameters

R
# 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:

100%
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
  1. Exact match: exp = 3 (most specific)
  2. Prefix Matching: e = 3exp (fewer keystrokes)
  3. Position Matching: First unnamed parameter → base (least secure)
💡 Tip: Use exact matches for production code (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:

R
# 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
💡 Tip: ... 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:

R
add <- function(a, b) {
  a + b   # Last line,Auto-Return
}

add(3, 4)
# [1] 7

(2) Explicit return()

R
# 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):

R
# 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.
💡 Tip: <- Assigning a value using invisible()x <- 5 does not print the result.



6. Anonymous Functions: Lambda Expressions

R supports anonymous functions (functions without names):

R
# 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
💡 Tip: Anonymous functions are extremely common in the 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

R
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

R
# 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

R
# 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
💡 Tip: Functions as return values are a core concept of R "functional programming," and many functions in the tidyverse (map() reduce()) make use of this.



8. do.call(): Dynamic Calling

do.call() Expand a list/vector into function arguments:

R
# 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:

R
# 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
⚠️ Note: By default, R functions cannot modify global variables (unlike in Python 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

R 📖 Display only
# ============================================
# 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)
44 logic lines (exceeds 40-line limit, display only)

Expected Output (Excerpt):

TEXT 📖 Display only
=================================
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

Q Do R functions have to use return()?
A No. The last expression in an R function is automatically returned, so you can use it without writing return(). However, using return() provides greater clarity, especially when returning early from an if condition.
Q Where should default parameters be placed?
A Default parameters must be placed after non-default parameters. 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.
Q How do you use ...?
A ... 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().
Q Can a function modify global variables?
A By default, no. You can use <<- 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.
Q When should you use anonymous functions?
A They are most commonly used in functions like lapply(), sapply(), map(), etc.

📖 Summary


📝 Exercises

  1. 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.

  2. 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.

  3. 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.

  4. Advanced Problem: Write a function analyze_scores(scores) that takes a vector of numbers and returns a list containing mean median max min pass_rate pass rates, using invisible() to prevent automatic printing. After calling the function, use result$pass_rate to access the pass rates.

  5. Challenge: Write a function that returns a function make_power(n) that returns function(x) x^n. Use make_power(2) to create square, use make_power(3) to create cube, and test square(5) = 25 and cube(3) = 27. Save a screenshot.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏