21 函数
21.1 函数类型
R 语言中函数主要有内置函数(builtin) 、 特殊函数(special )和 闭包函数(closure)三种类型
21.2 函数组件
-
formals()
参数列表 -
body()
函数体,包括异常处理、执行程序 -
environment()
确定函数如何查找与名称相关联的值的数据结构,俗称环境
Show the code
f02 <- function(x, y) {
# 注释
x + y
}
formals(f02)
#> $x
#>
#>
#> $y
body(f02)
#> {
#> x + y
#> }
environment(f02)
#> <environment: R_GlobalEnv>
属性 srcref
(source reference ),指向函数的源代码
Show the code
attributes(f02)
#> $srcref
#> function(x, y) {
#> # 注释
#> x + y
#> }
21.3 原函数
在R中,原函数(primitive functions) 不具备上述三个组件,而是直接通过.Primitive()调用C语言代码实现。
Show the code
formals(sum)
#> NULL
body(sum)
#> NULL
environment(sum)
#> NULL
attributes(sum)
#> NULL
在R语言中,“Primitive functions”(始函数)是使用C语言实现的核心函数,它们是R的基础并且直接映射到底层操作,从而提供了更高的运行效率。原始函数的类型为builtin或special,并且它们通常不接受延迟求值的参数,而是在调用时立即求值 。
原函数有内置函数(builtin) 和 特殊函数(special )两种类型。
Show the code
sum
#> function (..., na.rm = FALSE) .Primitive("sum")
typeof(sum)
#> [1] "builtin"
.Primitive("sum")
#> function (..., na.rm = FALSE) .Primitive("sum")
get("sum",envir = baseenv())
#> function (..., na.rm = FALSE) .Primitive("sum")
`[`
#> .Primitive("[")
typeof(`[`)
#> [1] "special"
.Primitive("[")
#> .Primitive("[")
21.4 First-class functions
在编程语言中,“First-class functions”(一级函数或函数作为一等公民)是函数式编程的一个核心概念,它意味着函数可以像任何其他类型的数据(如数字、字符串和对象)一样被传递和操作。具体来说,一级函数具有以下几个关键特性:
可赋值:可以给函数分配一个变量。
可传递:可以作为参数传递给其他函数。
可返回:可以从其他函数中作为返回值。
可创建:可以在运行时创建新函数。
这些特性使得一级函数非常灵活,并且允许编程者以声明式的方式构建复杂的逻辑。
R语言是一种支持函数式编程的语言,它自然地支持一级函数。以下是R语言中一级函数的一些应用示例:
-
将函数赋值给变量:
Show the code
f01 <- function(x) { x^2 }
-
将函数作为参数传递:
Show the code
apply_func <- function(x, f) { f(x) } result <- apply_func(10, f01) # 将函数f01()作为参数传递
-
从函数中返回函数:
Show the code
create_multiplier <- function(n) { function(x) { x * n } } double <- create_multiplier(2) result <- double(10) # 返回20
-
匿名函数 anonymous function:
-
Lambda函数:在较新版本的R中,可以使用
~
来定义Lambda函数,是一种简洁的匿名函数定义方式。Show the code
map_dbl(1:5, ~.x * 2) #> [1] 2 4 6 8 10
-
函数的高阶特性:
在R中,
vapply
、sapply
、lapply
、mapply
等函数都是高阶函数的例子,它们可以接受另一个函数作为参数,并对数据集执行操作。
21.4.1 一级函数的优势:
代码复用:通过将函数作为参数,可以编写更通用、更可重用的代码。
模块化:一级函数有助于将程序分解为更小、更易于管理的部分。
抽象:它们允许开发者创建更高级别的抽象,简化复杂逻辑的表达。
灵活性:一级函数提供了编写灵活和动态代码的能力。
函数式编程和一级函数是现代编程范式的重要组成部分,它们在数据处理和统计计算中尤其有用,这也是R语言作为统计计算语言的一个强大特性。
21.4.2 函数列表
Show the code
funs <- list(
half = function(x) x / 2,
double = function(x) x * 2
)
funs$double(10)
#> [1] 20
21.4.3 函数闭包
在 R 中,你经常会看到称为 闭包(closure)(内嵌在函数体的函数) 的函数。
函数是对象,使用创建函数闭包(function closure)时的即存在的状态值进行封装的函数。
Show the code
typeof(ggplot)
#> [1] "closure"
21.5 函数调用
21.6 词法作用域
词法作用域lexical scoping ,即函数运行中需要使用某个变量时,从其定义时的环境向外层逐层查找,而不是在调用时的环境中查找。这种查找找到变量对应的存储空间后,使用其当前值,而不是历史值,这种规则称为动态查找(dynamic lookup)
函数的作用域(scope)在R中称为环境。当一个函数被创建后,R 中存在一个让这个函数发挥作用的环境。
在启动 R 之后,我们就进去了一个全局环境之中(Global Environment),我们创建的各自变量、函数都会处于其中。
对象可以通过参数传递到函数中,但不会被函数改变。传递的是对象的副本而不是对象本身。除非使用 <<-
特殊赋值符。
Show the code
x <- 2
y <- 3
z <- 4
f <- function(w){
z <- 2
x <- w*y*z
return(x)
}
f(x)
#> [1] 12
x
#> [1] 2
y
#> [1] 3
z
#> [1] 4
Lexical scoping:
- Name masking
- Functions versus variables
- A fresh start
- Dynamic lookup
21.6.1 Name masking
在函数内部定义的名称会掩盖在函数外部定义的名称。
Show the code
x <- 10
y <- 20
g02 <- function() {
x <- 1
y <- 2
c(x, y)
}
g02()
#> [1] 1 2
如果未在函数中定义名称,则 R 将向上查找。
Show the code
x <- 2
g03 <- function() {
y <- 1
c(x, y)
}
g03()
#> [1] 2 1
y
#> [1] 20
21.7 惰性求值
Lazy evaluation 是一种将操作延迟到真正需要它的结果时才执行的优化技术。
惰性求值由称为 promise 或thunk 的数据结构提供支持。
21.7.1 promise
一个表达式,调用函数的环境,一个值,局部变量作用域求值
21.8 传递参数
关键字传参,位置传参
21.8.1 默认参数
21.8.2 缺失参数
21.9 特殊参数...
variable arguments,有了它,函数可以接受任意数量的附加参数。还可以将附加参数传递给另一个函数
使用list()
将传入的 ...
转换为列表,然后再进行处理
特殊形式 ..N
,按位置引用参数,使用 ..1、..2 等直接引用 … 对象中的第 1 个元素、第 2 个元素。
list(...)
计算参数并将它们存储在列表中
21.10 退出函数
返回一个对象,或者,错误
21.10.1 隐式返回值
最后计算的表达式是返回值
Show the code
j01 <- function(x) {
if (x < 10) {
0
} else {
10
}
}
j01(5)
#> [1] 0
j01(15)
#> [1] 10
21.10.2 显示返回值
调用return()
21.10.3 不可见值
大多数函数都以可见方式返回:在交互式上下文中调用函数会自动打印结果
Show the code
j03 <- function() 1
j03()
#> [1] 1
通过应用invisible()
于最后一个值来阻止自动打印:
Show the code
j04 <- function() invisible(1)
j04()
若要验证此值是否确实存在,可以显式打印它或将其括在括号中,或者使用返回值和可见性标志:withVisible()
Show the code
print(j04())
#> [1] 1
(j04())
#> [1] 1
withVisible(j04())
#> $value
#> [1] 1
#>
#> $visible
#> [1] FALSE
不可见返回的最常见函数是:<-
Show the code
a <- 2
(a <- 2)
#> [1] 2
a <- b <- c <- d <- 2
21.10.4 Errors
如果函数无法完成其分配的任务,则应抛出错误,并立即终止函数的执行。
21.10.5 退出处理程序
21.11 函数形式
- prefix:
foofy(a, b, c)
- infix:
x + y
- replacement:
names(df) <- c("a", "b", "c")
- special:
[[
,if
,for
21.11.1 前缀形式
是 R 代码中最常见的形式,参数按确切名称匹配,然后与唯一前缀匹配,最后按位置匹配。
Show the code
k01 <- function(abcdef, bcde1, bcde2) {
list(a = abcdef, b1 = bcde1, b2 = bcde2)
}
str(k01(2, 3, abcdef = 1))
#> List of 3
#> $ a : num 1
#> $ b1: num 2
#> $ b2: num 3
str(k01(2, 3, a = 1)) # 部分匹配
#> List of 3
#> $ a : num 1
#> $ b1: num 2
#> $ b2: num 3
options(warnPartialMatchArgs = TRUE)
x <- k01(a = 1, 2, 3)
21.11.2 中缀形式
Show the code
`%+%` <- function(a, b) paste0(a, b)
"new " %+% "string"
#> [1] "new string"
21.11.3 替换形式
Show the code
`second<-` <- function(x, value) {
x[2] <- value
x
}
x <- 1:10
second(x) <- 5L
x
#> [1] 1 5 3 4 5 6 7 8 9 10
21.11.4 特殊形式
所有特殊形式都作为原函数 实现(即在 C 中),这意味着打印这些函数没有信息。
Show the code
`for`
#> .Primitive("for")
formals(`for`)
#> NULL