R 编程中的词法作用域
R 编程中的词法作用域意味着在定义函数的环境中搜索自由变量的值。环境是符号、值和对的集合,每个环境都有一个父环境,一个环境可能有多个子环境,但没有父环境的唯一环境是空环境。如果在定义函数的环境中找不到符号的值,则在父环境中继续搜索。在 R 中,通过首先查看创建函数的环境来解析自由变量绑定。这称为词法作用域。
为什么要词法作用域?
词法作用域是一组有助于确定 R 如何表示符号值的规则。它是 R 中的一个内置规则,可在语言级别自动工作。它主要用于指定统计计算。词法作用域根据函数最初创建时的嵌套方式而不是调用时的嵌套方式来查找符号。当我们使用词法作用域时,我们不必知道函数是如何被调用的,也不必弄清楚变量的值将在哪里查看。我们只需要查看函数的定义。
词汇范围中的词汇含义与通常的英语定义完全不同,后者意味着与一种语言的单词或词汇相关,这与语法和结构不同,而是来自计算机科学术语词法含义,通过该过程将表示为文本的代码转换为有意义的片段这可以通过编程语言来理解。考虑以下示例:
f <- function(x, y)
{
x * y * z
}
在这:
- x 和 y 是形式参数
- z 作为自由变量
因此,语言的范围规则决定了如何将值分配给自由变量。自由变量不是形式参数,也不是在函数体内分配的局部变量。
词法作用域的原则
R 实现词法作用域的背后有四个基本原则:
- 名称屏蔽
- 函数与变量
- 一个新的开始
- 动态查找
让我们一一讨论每个原则。
名称屏蔽
以下示例说明了词法作用域的最基本原理,您应该可以毫无问题地预测输出。
- 如果变量未在函数内部定义:
例子:c <- 10 f <- function(a, b) { a + b + c } f(8, 5)
输出:
[1] 23
它将 c 值设为 10,然后将这些数字相加,最后我们得到 23 作为输出。
- 如果函数内部没有定义名称:
如果名称没有在函数内部定义,R 将向上查找一级。
例子:a <- 10 b <- function() { c <- 11 c(a, c) } b()
输出:
[1] 10 11
- 当一个函数在另一个函数中定义时:
如果一个函数是在另一个函数中定义的,同样的规则也适用:查看当前函数的内部,然后是该函数的定义位置,依此类推,一直到全局环境,然后是其他加载的包。
例子:a <- 10 g <- function(){ b <- 20 h <- function(){ c <- 30 c(a, b, c) } h() } g()
输出:
[1] 10 20 30
- 当函数由另一个函数创建时:
相同的规则适用于闭包,由其他函数创建的函数。
例子:a <- function(z){ b <- 10 function(){ z + 4 * b } } x <- a(10) x()
输出:
[1] 50
R 在调用函数后返回 b 的准确值,因为 x 保留了定义它的环境。环境包括 b 的值。
函数与变量
无论关联值的类型如何,相同的原则都适用——查找函数的工作方式与查找变量的方式完全相同:
例子:
a <- function(x) 10 * x
b <- function(){
a <- function(x) x + 10
a(12)
}
b()
输出:
[1] 22
一个新的开始
调用函数时,每次都会创建一个新环境。每个确认都是完全独立的,因为函数无法判断上次运行时发生了什么。
例子:
a <- function(){
if(!exists("z"))
{
z <- 10
}
else
{
z <- z+10
}
z
}
a()
输出:
[1] 10
动态查找
词法作用域控制在哪里寻找值而不是何时寻找它们。 R 在执行函数时而不是在创建函数时查找值。函数的输出可能会根据其环境之外的对象而有所不同。
例子:
g <- function() x^3
x <- 10
g()
输出:
[1] 1000
R 中有一个函数,它是 codetools 中的codetools
findGlobals()
,它可以帮助我们找到函数中使用的所有全局变量并列出函数的所有外部依赖项。 findGlobals()
查找闭包使用的全局变量和函数。
例子:
aGlobal <- rnorm(10)
bGlobal <- rnorm(10)
f <- function()
{
a <- aGlobal
b <- bGlobal
plot(b ~ a)
}
codetools::findGlobals(f)
输出:
[1] "{" "~" "<-" "aGlobal" "bGlobal" "plot"
我们可以手动将环境更改为空环境emptyenv()
。 emptyenv()
是一个完全空的环境。