为什么 R 无法通过词法范围找到参数的值?
Why can R not find the value of an argument through lexical scoping?
我一直在阅读 Hadley Wickham 的 Advanced R,以便更好地了解 R 的机制及其背后的工作原理。到目前为止,我很喜欢它,一切都很清楚。有一个问题占据了我的脑海,我还没有找到解释。
我非常熟悉 R 的范围规则,它决定了如何将值分配给自由变量。然而,我一直在努力解决为什么 R 在第一种情况下无法通过词法范围找到形式参数的值的问题。考虑以下示例:
y <- 4
f1 <- function(x = 2, y) {
x*2 + y
}
f1(x = 3)
它通常会抛出错误,因为我没有为参数 y
分配默认值。但是,如果我在函数体中创建一个局部变量 y
,它不会抛出任何错误:
我还在 Professeur Matloff 的书中读到,参数就像局部变量一样,所以这就是为什么这个问题对我来说仍然是个谜。
f1 <- function(x = 2, y) {
y <- 4
x*2 + y
}
f1(x = 3)
这里也没有错误,原因很清楚:
y <- 2
f2 <- function(x = 2) {
x*2 + y
}
f2()
非常感谢您。
注意R只会在你去使用变量的时候抛出错误。如果你有
f1 <- function(x = 2, y) {
x*2 + 5
}
f1(x = 3)
# [1] 11
一切都会好起来的。那是因为该参数是一个“承诺”,在您实际使用它之前不会得到解决。这使您可以执行
之类的操作
f1 <- function(x = 2, y=x+5) {
x*2 + y
}
f1(x = 3)
# [1] 14
其中 y
值实际上将使用 x
的值,该值在评估 promise 时传递给函数。此外你还可以做
f1 <- function(x = 2, y=z+2) {
z <- x + 10
x*2 + y
}
f1(x = 3)
[1] 21
其中 y
能够获取调用函数时甚至不存在的 z
的值。同样,这是因为参数值是承诺的,并且仅在实际使用时才进行评估。他们在评估时可以访问环境中的所有值。但请注意,这只有效,因为默认参数值是在函数体的上下文中计算的。这与将值传递给函数时不同。在那种情况下,值是在调用环境中计算的,而不是本地函数体。所以你做不到
f1(x = 3, y=z+2)
# Error in f1(x = 3, y = z + 2) : object 'z' not found
您在第一个函数中出现错误的原因是当您尝试在 x*2 + y
中使用它时 y
的值不存在。由于您已将 y
定义为参数,因此它不再是“自由”变量,并且不会在父作用域中查找。您不会在第二个函数中遇到错误,因为您已将 y
变量重新绑定到局部函数变量,因此您根本不会使用参数值。
如果你运行
f1 <- function(x = 2, y) {
y <- 4
x*2 + y
}
f1(x = 3, y=200)
# [1] 10
2000基本没了。重新分配 y
后,您将无法再访问该值。 R 在重新定义之前不会检查变量是否已经存在,因此没有任何东西会尝试评估函数参数的承诺值 y
。
一旦对 promise 进行了评估,参数将像局部变量一样起作用。
我认为 R 在这里非常一致。
只要不调用 y
,R 就不会理会这个叫做 promise 的东西。
f1 <- function(x = 2, y) {
x*2 + 5
}
f1(x = 3)
# [1] 11
在您的第一个示例中,y 不能从全局环境派生,因为 y 是在函数环境内部本地定义的。由于缺少该值,将引发错误
y <- 4
f2 <- function(x = 2, y) {
x*2 + y
}
f2(x = 3)
#> Error in f2(x = 3) : argument "y" is missing, with no default
这可能会导致类似这样的情况,即“隐藏”容易出错的设置,直到用户不小心偶然发现它。
f3 <- function(x = 2, y) {
if (x >= 2) {
y <- 4
}
x * 2 + y
}
f3(x = 2)
#> 8
这种情况很难用单元测试来覆盖,这就是为什么你经常看到函数体顶部带有 force()
的原因。
f4 <- function(x = 2, y) {
force(y)
if (x > 2) {
y <- 4
}
x * 2 + y
}
f4(x = 2)
#> Error in force(y) : argument "y" is missing, with no default
有一个不错的 blog post 相当广泛地涵盖了惰性求值。
我一直在阅读 Hadley Wickham 的 Advanced R,以便更好地了解 R 的机制及其背后的工作原理。到目前为止,我很喜欢它,一切都很清楚。有一个问题占据了我的脑海,我还没有找到解释。 我非常熟悉 R 的范围规则,它决定了如何将值分配给自由变量。然而,我一直在努力解决为什么 R 在第一种情况下无法通过词法范围找到形式参数的值的问题。考虑以下示例:
y <- 4
f1 <- function(x = 2, y) {
x*2 + y
}
f1(x = 3)
它通常会抛出错误,因为我没有为参数 y
分配默认值。但是,如果我在函数体中创建一个局部变量 y
,它不会抛出任何错误:
我还在 Professeur Matloff 的书中读到,参数就像局部变量一样,所以这就是为什么这个问题对我来说仍然是个谜。
f1 <- function(x = 2, y) {
y <- 4
x*2 + y
}
f1(x = 3)
这里也没有错误,原因很清楚:
y <- 2
f2 <- function(x = 2) {
x*2 + y
}
f2()
非常感谢您。
注意R只会在你去使用变量的时候抛出错误。如果你有
f1 <- function(x = 2, y) {
x*2 + 5
}
f1(x = 3)
# [1] 11
一切都会好起来的。那是因为该参数是一个“承诺”,在您实际使用它之前不会得到解决。这使您可以执行
之类的操作f1 <- function(x = 2, y=x+5) {
x*2 + y
}
f1(x = 3)
# [1] 14
其中 y
值实际上将使用 x
的值,该值在评估 promise 时传递给函数。此外你还可以做
f1 <- function(x = 2, y=z+2) {
z <- x + 10
x*2 + y
}
f1(x = 3)
[1] 21
其中 y
能够获取调用函数时甚至不存在的 z
的值。同样,这是因为参数值是承诺的,并且仅在实际使用时才进行评估。他们在评估时可以访问环境中的所有值。但请注意,这只有效,因为默认参数值是在函数体的上下文中计算的。这与将值传递给函数时不同。在那种情况下,值是在调用环境中计算的,而不是本地函数体。所以你做不到
f1(x = 3, y=z+2)
# Error in f1(x = 3, y = z + 2) : object 'z' not found
您在第一个函数中出现错误的原因是当您尝试在 x*2 + y
中使用它时 y
的值不存在。由于您已将 y
定义为参数,因此它不再是“自由”变量,并且不会在父作用域中查找。您不会在第二个函数中遇到错误,因为您已将 y
变量重新绑定到局部函数变量,因此您根本不会使用参数值。
如果你运行
f1 <- function(x = 2, y) {
y <- 4
x*2 + y
}
f1(x = 3, y=200)
# [1] 10
2000基本没了。重新分配 y
后,您将无法再访问该值。 R 在重新定义之前不会检查变量是否已经存在,因此没有任何东西会尝试评估函数参数的承诺值 y
。
一旦对 promise 进行了评估,参数将像局部变量一样起作用。
我认为 R 在这里非常一致。
只要不调用 y
,R 就不会理会这个叫做 promise 的东西。
f1 <- function(x = 2, y) {
x*2 + 5
}
f1(x = 3)
# [1] 11
在您的第一个示例中,y 不能从全局环境派生,因为 y 是在函数环境内部本地定义的。由于缺少该值,将引发错误
y <- 4
f2 <- function(x = 2, y) {
x*2 + y
}
f2(x = 3)
#> Error in f2(x = 3) : argument "y" is missing, with no default
这可能会导致类似这样的情况,即“隐藏”容易出错的设置,直到用户不小心偶然发现它。
f3 <- function(x = 2, y) {
if (x >= 2) {
y <- 4
}
x * 2 + y
}
f3(x = 2)
#> 8
这种情况很难用单元测试来覆盖,这就是为什么你经常看到函数体顶部带有 force()
的原因。
f4 <- function(x = 2, y) {
force(y)
if (x > 2) {
y <- 4
}
x * 2 + y
}
f4(x = 2)
#> Error in force(y) : argument "y" is missing, with no default
有一个不错的 blog post 相当广泛地涵盖了惰性求值。