Functions
## Learning Objectives
- Create and call functions
- Understand parameters and arguments
- Master function scoping
- Work with anonymous functions
- Handle variable arguments
## Defining Functions
### Basic Function
```r
# Function definition
greet <- function() {
print("Hello, World!")
}
# Call function
greet()
```
### Function with Parameters
```r
# Single parameter
greet <- function(name) {
paste("Hello,", name)
}
greet("Alice") # "Hello, Alice"
# Multiple parameters
add <- function(a, b) {
a + b
}
add(3, 5) # 8
```
### Default Parameters
```r
# Default values
greet <- function(name = "World") {
paste("Hello,", name)
}
greet() # "Hello, World"
greet("Alice") # "Hello, Alice"
# Multiple defaults
power <- function(base, exp = 2) {
base ^ exp
}
power(3) # 9
power(3, 3) # 27
```
## Return Values
### Explicit Return
```r
# Using return()
absolute <- function(x) {
if (x < 0) {
return(-x)
}
return(x)
}
absolute(-5) # 5
absolute(5) # 5
```
### Implicit Return
```r
# Last expression returned automatically
absolute <- function(x) {
if (x < 0) -x else x
}
# Common mistake
get_info <- function(x) {
if (x > 0) {
print("positive") # NULL returned!
}
"done"
}
```
### Returning Multiple Values
```r
# Return as list
stats <- function(x) {
list(
mean = mean(x),
median = median(x),
sd = sd(x)
)
}
result <- stats(1:10)
result$mean # 5.5
result$median # 5.5
```
## Named Arguments
### Using Names
```r
# Call with names
power(base = 3, exp = 2)
# Order doesn't matter with names
power(exp = 2, base = 3)
# Mix positional and named
power(3, exp = 2)
```
### Partial Matching
```r
power <- function(base = 2, exponent = 1) {
base ^ exponent
}
power(e = 3) # Works: e matches exponent
```
## Scoping
### Environment Basics
```r
# Variables outside function are visible
global_var <- 10
my_func <- function() {
print(global_var) # 10
}
my_func()
```
### Local Variables
```r
my_func <- function() {
local_var <- 20 # Only exists inside function
print(local_var)
}
my_func()
# global_var # Error: object not found
```
### Assignment Creates Local
```r
x <- 10
my_func <- function() {
x <- 20 # Creates new local variable
print(x) # 20
}
my_func()
print(x) # 10 (unchanged)
```
### Using <<- for Global Assignment
```r
x <- 10
my_func <- function() {
x <<- 20 # Modifies global variable
print(x)
}
my_func() # 20
print(x) # 20
```
## Anonymous Functions
### No Name Functions
```r
# Anonymous function
function(x) x ^ 2
# Assign to variable
square <- function(x) x ^ 2
# Or use anonymous directly
(function(x) x ^ 2)(5) # 25
```
### In Apply Functions
```r
nums <- list(a = 1:3, b = 4:6)
# Anonymous in lapply
lapply(nums, function(x) sum(x) / length(x))
# Anonymous in sapply
sapply(1:3, function(x) if (x %% 2 == 0) "even" else "odd")
```
## Variable Arguments
### ... (Ellipsis)
```r
# Pass through arguments
print_all <- function(...) {
args <- list(...)
for (arg in args) {
print(arg)
}
}
print_all(1, "hello", TRUE, c(1, 2, 3))
```
### Partial Application
```r
# Fix some arguments
power <- function(base, exp) {
base ^ exp
}
square <- function(x) power(x, 2)
cube <- function(x) power(x, 3)
square(5) # 25
cube(5) # 125
```
## Function as Return Value
### Returning Functions
```r
# Function factory
make_power <- function(exp) {
function(x) {
x ^ exp
}
}
square <- make_power(2)
cube <- make_power(3)
square(5) # 25
cube(5) # 125
```
## Function Operators
### Pipe with Functions
```r
# R 4.1+ native pipe
1:10 |> sum() |> sqrt()
# Older style
library(magrittr)
1:10 %>% sum() %>% sqrt()
```
## Closure
### Capturing Environment
```r
# Counter example using closure
make_counter <- function() {
count <- 0
function() {
count <<- count + 1
count
}
}
counter1 <- make_counter()
counter2 <- make_counter()
counter1() # 1
counter1() # 2
counter1() # 3
counter2() # 1 (separate count)
```
## Vectorized Functions
### Making Functions Vectorized
```r
# Your function only takes single value
add_one <- function(x) x + 1
# Vectorize it
add_one_vectorized <- Vectorize(add_one)
add_one_vectorized(1:5) # 2 3 4 5 6
```
## Error Handling
### tryCatch
```r
# Basic error handling
safe_divide <- function(a, b) {
tryCatch({
if (b == 0) stop("Division by zero")
a / b
},
error = function(e) {
message("Error: ", e$message)
NA
})
}
safe_divide(10, 2) # 5
safe_divide(10, 0) # NA with message
```
### try for Debugging
```r
# try continues even on error
result <- try({
# Risky code
x <- 1:3
y <- c("a", "b")
x + y # Error!
})
if (inherits(result, "try-error")) {
print("Operation failed")
}
```
## Summary
- Functions defined with `function()` and assigned to variables
- Parameters can have default values
- Use `return()` for explicit returns (or last expression)
- Use `list()` to return multiple values
- Scoping: local variables shadow global
- Use `<<-` to modify global variables
- Anonymous functions useful in apply family
- Use `...` for variable arguments
- `Vectorize()` makes functions work on vectors
- `tryCatch()` for error handling
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →