Programming for Applications

Chapter 9: Functions

Yu-You Liou (NTU)

Shih Chien University

2026-08-03

The Workhorses

The Function Keyword

Functions evaluate input arguments and return an output value. The definition syntax is

function(arguments) body

where arguments is a set of symbol names (optionally with default values) available inside the body, and body is an R expression. Braces are customary but optional for a single expression — these two are equivalent:

f <- function(x, y) x + y
f <- function(x, y) { x + y }

Arguments

Defaults Make Arguments Optional

Give an argument a default value and callers may omit it; omit a required argument and the error arrives only when the function tries to use it:

f <- function(x, y) { x + y }
f(1, 2)
[1] 3
g <- function(x, y = 10) { x + y }
g(1)
[1] 11
g(1, 2)     # overriding the default
[1] 3
f(1)        # missing y: error when `+` needs it
Error in `f()`:
! 缺少引數 "y",也沒有預設值

Note

A function that simply never touches an uninitialized argument runs fine. You can even detect missing arguments yourself — e.g., inspecting as.list(match.call()) for a NULL entry — but in practice, put defaults in the signature: it keeps functions clear and readable.

Variable-Length Argument Lists: …

The ellipsis ... means “all the other arguments.” Two main uses. First, passing extras through to another function — here everything after x flows into summary:

v <- c(sqrt(1:100))
f <- function(x, ...) { print(x); summary(...) }
f("Here is the summary for v.", v, digits = 2)
[1] "Here is the summary for v."
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    1.0     5.1     7.1     6.7     8.7    10.0 

(Recall from Chapter 7 that ... is a special object type, manipulable only inside a function body.)

Reading the Arguments in …

Second, you can consume the variable arguments yourself, by converting ... to a list. A function that sums all its arguments:

addemup <- function(x, ...) {
  args <- list(...)
  for (a in args) x <- x + a
  x
}
addemup(1, 1)
[1] 2
addemup(1, 2, 3, 4, 5)
[1] 15

Individual items are also reachable directly as ..1, ..2, … (any ..n works). And named arguments remain valid symbols inside the body (scoping: Chapter 8).

Return Values

An explicit return works as in other languages — but R simply returns the last evaluated expression, so return is commonly omitted:

f <- function(x) { return(x^2 + 3) }
f(3)
[1] 12
f <- function(x) { x^2 + 3 }
f(3)
[1] 12

Use the explicit form when it makes the code cleaner — e.g., early exits.

Functions as Arguments

Higher-Order Functions: sapply

Many R functions accept other functions as arguments (modeling functions, for instance, take a function describing how to treat missing values). The classic example is sapply, which applies a function to every element of a vector:

a <- 1:7
sapply(a, sqrt)
[1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751

A toy example — sqrt(1:7) does the same — but many useful functions don’t accept multi-element vectors, and sapply extends them gracefully. (The whole family of summarizing relatives: Chapter 12.)

Anonymous Functions

Functions are objects, so they don’t need names. A function passed without ever being named is anonymous. A minimal demonstration — a function that applies its argument to the number 3:

apply.to.three <- function(f) { f(3) }
apply.to.three(function(x) { x * 7 })
[1] 21

How the interpreter reads it: f is bound to the anonymous function(x) {x * 7}; evaluating f(3) binds x to 3; the body returns 3 * 7 = 21.

Anonymous Functions with sapply

The everyday use: passing one-off functions to apply-style functions:

a <- c(1, 2, 3, 4, 5)
sapply(a, function(x) { x + 1 })
[1] 2 3 4 5 6

This family is a real alternative to control structures. Squaring each element with a loop versus an apply:

v <- 1:20
w <- NULL
for (i in 1:length(v)) { w[i] <- v[i]^2 }
w
 [1]   1   4   9  16  25  36  49  64  81 100 121 144 169 196 225 256 289 324 361
[20] 400
w <- sapply(v, function(i) { i^2 })
w
 [1]   1   4   9  16  25  36  49  64  81 100 121 144 169 196 225 256 289 324 361
[20] 400

The second version states its intent — apply this function to each element — and is faster, too (Chapter 24).

Applying Anonymous Functions Directly

You can define an anonymous function and call it on the spot — but the function object must be wrapped in parentheses:

(function(x) { x + 1 })(1)
[1] 2

Why? Function calls f(arguments) bind very tightly. Without the parentheses, function(x) {x+1}(1) quietly defines a function whose body is {x+1}(1) — no error appears, because the body isn’t evaluated yet. Valid R, almost certainly not what you meant.

Properties of Functions

args and formals

To see what arguments a function accepts, args returns a function object with a NULL body:

args(sin)
function (x) 
NULL
args(args)
function (name) 
NULL
args(lm)
function (formula, data, subset, weights, na.action, method = "qr", 
    model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE, 
    contrasts = NULL, offset, ...) 
NULL

To manipulate the argument list from code, use formals, which returns a pairlist — one pair per argument, value NULL where no default exists. (formals works only on closures, not built-ins.)

f <- function(x, y=1, z=2) { x + y + z }
f.formals <- formals(f)
f.formals
$x


$y
[1] 1

$z
[1] 2
f.formals$y
[1] 1

Changing Arguments: formals<- and alist

formals also works on the left-hand side of an assignment — change a default in place:

f.formals$y <- 3
formals(f) <- f.formals
args(f)
function (x, y = 3, z = 2) 
NULL

The convenience function alist builds an argument list using definition syntax (note: an argument with no default still needs its equals sign):

formals(f) <- alist(x=, y=100, z=200)
f
function (x, y = 100, z = 200) 
{
    x + y + z
}

body

The body, too, is readable and writable — it is a language object (typeof(body(f)) is "language"); assign quoted code (the book uses expression(), which also works):

body(f)
{
    x + y + z
}
body(f) <- expression({x * y * z})
f
function (x, y = 100, z = 200) 
{
    x * y * z
}

Functions are data, all the way down: arguments and body alike can be inspected and rewritten by ordinary R code.

Argument Order and Named Arguments

Three Ways to Supply Arguments

Inside a function, arguments are accessed by the names from the definition:

addTheLog <- function(first, second) { first + log(second) }

Callers may specify arguments three ways, matched in priority order:

addTheLog(second=exp(4), first=1)   # 1. exact names (matched first)
[1] 5
addTheLog(s=exp(4), f=1)            # 2. partially matching names
[1] 5
addTheLog(1, exp(4))                # 3. positional order
[1] 5

One caution: with generic functions the name of the dispatching argument varies between methods (x, object, …), so passing the dispatching object positionally is the most portable habit.

Prefer Exact Names; Partial Matching Bites

Exact names cost keystrokes but buy readability and remove ambiguity. Partial matching is discouraged for good reason (R can warn about it via options(warnPartialMatchArgs=TRUE)):

f <- function(arg1=10, arg2=20) {
  print(paste("arg1:", arg1))
  print(paste("arg2:", arg2))
}
f(arg=1)            # ambiguous: error
Error in `f()`:
! 引數 1 有多個與之相對應的形式引數
f(arg=1, arg2=2)    # now "arg" can only mean arg1...
[1] "arg1: 1"
[1] "arg2: 2"
f(arg=1, arg1=2)    # ...or suddenly it means arg2!
[1] "arg1: 2"
[1] "arg2: 1"

The same prefix lands on different arguments depending on what else is supplied — a recipe for silent bugs.

Side Effects

Beyond the Return Value

Every function returns a value; some also change variables in other environments, draw graphics, read or write files, or talk to the network. These extra actions are side effects.

The key side-effecting operator is <<-. The statement var <<- value skips the current environment and searches the enclosing environments upward for an existing var; if the global environment is reached without a match, the assignment happens there:

rm(list = ls())
doesnt.assign.x <- function(i) { x <- i }
doesnt.assign.x(4)
x                                   # <- stayed local: not found
Error:
! 找不到物件 'x'
assigns.x <- function(i) { x <<- i }
assigns.x(4)
x                                   # <<- escaped to the global environment
[1] 4

I/O and Graphics

Two more families of side effects round out the picture:

  • Input/output. Loading data from files or the Internet, saving data to disk — actions beyond returning an object. The full treatment is Chapter 11.
  • Graphics. Plotting functions may return objects, but their point is the drawing — on screen or to a file. Chapters 13 and 14 cover them.

Tip

Design habit: keep most functions pure (no side effects) and concentrate the side effects in a few clearly named places. Pure functions are easier to test, reuse, and parallelize.