函数是 R 语言中的基本构建块 ,所有的操作都是函数调用, 比如+, 控制流运算符,比如for、 if 和 while, 取子集操作符,比如[]和$,甚至花括号{
函数的组成
func <- function(x){
x <- x + 1
}
r语言的函数由三部分组成
- 函数体, body(),函数的代码。
- 参数, formals(),控制函数调用的参数列表。
- 环境, environment(),函数的变量所在位置的”地图”
原语函数
原语函数(Primitive functions),sum(),它直接使用了.Primitive()调用 C 语言代码,并且不包含 R 语言代码。 因此,它们的 formals()、 body()和 environment()都是 NULL ,原语函数仅仅存在于base包中
词法作用域
作用域是一组规则,这些规则控制着 R 语言如何查找符号的值
R 语言有两种类型的作用域:
- 词法作用域(Lexical scoping),在语言层面自动实现,跟函数创建密切相关
- 动态作用域(Dynamic scoping),用于在交互式分析情景下,在选择函数时,可以减少键盘输入。
词法作用域进行符号的值的查找,是基于在函数创建时是如何嵌套的,而不是它们在调用时如何嵌套的。 有四个基本原则
- 名字屏蔽
- 函数和变量
- 全新的开始状态
- 动态查找
名字屏蔽(name masking)
名字在函数中没有定义,那么 R 语言将向上一个层次查找。 首先,查看当前函数的内部,然后是这个函数被定义的环境,然后继续向上,以此类推,一直到全局环境,然后,再查找其它已经加载的包。 ```r f <- function() { x <- 1 y <- 2 c(x, y) }
f() [1] 1 2
f <- function(x){ y <- 1 function(){ c(x, y) } }
a <- f(100) a() [1] 100 1
a function(){ print(c(x, y)) }
ls(environment(a)) [1] “x” “y” ls(environment(f)) [1] “a” “f” “f2” “y”
<a name="ySebs"></a>
### 函数和变量
无论关联了什么类型的值,都适用同样的原则——搜索函数与搜索变量的过程完全一样,对于函数,规则有一点点调整。 如果很明显你要的是函数(例如,f(3)),那么在这样的语境中, R 语言在搜索时,将忽略那些不是函数的对象。
<a name="eNAzV"></a>
### 全新的开始状态
每一次函数被调用时,一个新的环境就会被创建出来,随后,函数会在该环境中执行。 函数无法报告它上一次调用时发生了什么,因为每次调用都是完全独立的
<a name="Udts2"></a>
### 动态查找
词法作用域决定了去哪里查找值,而不是决定在什么时候查找值。 R 语言在函数运行时查找值,而不是在函数创建时查找值。 这意味着,**函数的输出是可以随着它所处的环境外面的对象,而发生变化的 **<br />**通常应该避免这种行为,因为这意味着函数不再是独立的。 这是一种常见的错误**<br />**可以使用**codetools::findGlobals()查找外部依赖
```r
f <- function(){
x
}
x <- 8
> codetools::findGlobals(f)
[1] "{" "x"
函数参数
函数调用
当调用一个函数时,你可以通过参数的位置,或者通过完整的名称或者部分的名称,来匹配参数。 参数匹配的顺序是:首先是精确的名称匹配(完美匹配),然后通过前缀匹配,最后通过位置匹配。...
之后列出的参数都必须使用它们的全名, 前缀匹配能不用就不用
参数列表
使用do.call()
函数可以将一个参数列表传给相应的函数
do.call(mean, list(1:10, na.rm = TRUE))
#> [1] 5.5
# 相当于
mean(1:10, na.rm = TRUE)
默认参数和延迟计算
R 语言中的函数参数可以有默认值由于在 R 语言中参数都是延迟计算的,所以参数的默认值可以使用其它参数定义
中缀函数
R中大多数函数是前缀操作符, 即函数名称排在函数前,中缀函数,即函数名称位于参数之间,如 +
, %in%
,R中内置的中缀函数如下,
%%、 %*%、 %/%、 %in%、 %o%、 %x%
::, :::, $, @, ?, *, /, +, -, >, >=, <, <=, ==, !=, !, &, &&, |, ||, ~, <-, <<-
创建一个中缀函数(R 的默认优先规则意味着中缀操作符是从左到右进行结合的 )
`%+%` <- function(a, b) paste(a, b, sep = "")
"new" %+% " string"
#> [1] "new string
替换函数
替换函数的行为表现为就地修改,函数名称为xxx<-
`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
# 创建一个随机修改向量元素值的替换函数
`change<-` <- function(x, value){
x[sample(1:length(x), 1)] <- value
x
}
x <- c(1:12)
change(x) <- sample(1:1000, 1)
## 三次运行结果
[1] 444 2 3 4 5 6 7 8 9 10 11 12
[1] 444 486 3 4 5 6 7 8 9 10 11 12
[1] 444 486 3 4 5 6 7 8 9 10 11 930
返回值
函数中最后一个表达式的计算结果会成为函数的返回值,也就是调用函数的结果,纯函数(pure function):这种函数总是把相同的输入映射到相同的输出,并且不影响工作空间。 换句话说, 纯函数没有副作用:除了返回值外,它们不以任何方式影响其它状态