为什么必须使用两轮表达式引用来定义 local({...})?
Why must local({...}) be defined using two rounds of expression quoting?
我想了解 R 的 local
函数是如何工作的。有了它,您可以打开一个临时的本地范围,这意味着 local
中发生的事情(最值得注意的是,变量定义)保留在 local
中。只有区块的最后一个值被返回给外界。所以:
x <- local({
a <- 2
a * 2
})
x
## [1] 4
a
## Error: object 'a' not found
local
定义如下:
local <- function(expr, envir = new.env()){
eval.parent(substitute(eval(quote(expr), envir)))
}
据我了解,发生了两轮表达式引用和后续评估:
eval(quote([whatever expr input]), [whatever envir input])
由 substitute
作为未评估的调用生成。
- 调用在
local
的调用框架(在我们的例子中是全局环境)中进行评估,所以
[whatever expr input]
在 [whatever envir input]
中计算
但是,我不明白为什么第2步是必要的。为什么我不能像这样简单地定义 local
:
local2 <- function(expr, envir = new.env()){
eval(quote(expr), envir)
}
我认为它会在空环境中计算表达式 expr
?所以 expr
中定义的任何变量都应该存在于 envir
中,因此在 local2
?
结束后消失
但是,如果我尝试这样做,我会得到:
x <- local2({
a <- 2
a * 2
})
x
## [1] 4
a
## [1] 2
所以 a
泄漏到全球环境中。这是为什么?
编辑: 更神秘:为什么它不会发生:
eval(quote({a <- 2; a*2}), new.env())
## [1] 4
a
## Error: object 'a' not found
R 函数的参数作为承诺传递。除非特别要求该值,否则不会对其进行评估。所以看看
# clean up first
if exists("a") rm(a)
f <- function(x) print(1)
f(a<-1)
# [1] 1
a
# Error: object 'a' not found
g <- function(x) print(x)
g(a<-1)
# [1] 1
a
# [1] 1
请注意,在 g()
函数中,我们使用传递给函数的值,该值是对 a
的赋值,以便在全局环境中创建 a
。使用 f()
,永远不会创建该变量,因为该函数参数仍然是承诺结束,从未被评估。
如果你想访问一个参数而不评估它,你需要使用类似 match.call()
或 subsititute()
的东西。 local()
函数执行后者。
如果删除 eval.parent()
,您会看到替换将参数中未计算的表达式放入对 eval()
.
的新调用中
h <- function(expr, envir = new.env()){
substitute(eval(quote(expr), envir))
}
h(a<-1)
# eval(quote(a <- 1), new.env())
好像你在做什么
j<- function(x) {
quote(x)
}
j(a<-1)
# x
您并没有真正创建新的函数调用。此外,当您 eval()
该表达式时,您会从其原始调用环境触发 x
的评估(触发承诺的评估),而不是在新环境中评估表达式。
local()
然后使用 eval.parent()
以便您可以在块内的环境中使用现有变量。例如
b<-5
local({
a <- b
a * 2
})
# [1] 10
看看这里的行为
local2 <- function(expr, envir = new.env()){
eval(quote(expr), envir)
}
local2({a<-5; a})
# [1] 5
local2({a<-5; a}, list(a=100, expr="hello"))
# [1] "hello"
看看当我们使用非空环境时,eval()
在环境中查找 expr
,而不是在环境中计算代码块。
我想了解 R 的 local
函数是如何工作的。有了它,您可以打开一个临时的本地范围,这意味着 local
中发生的事情(最值得注意的是,变量定义)保留在 local
中。只有区块的最后一个值被返回给外界。所以:
x <- local({
a <- 2
a * 2
})
x
## [1] 4
a
## Error: object 'a' not found
local
定义如下:
local <- function(expr, envir = new.env()){
eval.parent(substitute(eval(quote(expr), envir)))
}
据我了解,发生了两轮表达式引用和后续评估:
eval(quote([whatever expr input]), [whatever envir input])
由substitute
作为未评估的调用生成。- 调用在
local
的调用框架(在我们的例子中是全局环境)中进行评估,所以[whatever expr input]
在[whatever envir input]
中计算
但是,我不明白为什么第2步是必要的。为什么我不能像这样简单地定义 local
:
local2 <- function(expr, envir = new.env()){
eval(quote(expr), envir)
}
我认为它会在空环境中计算表达式 expr
?所以 expr
中定义的任何变量都应该存在于 envir
中,因此在 local2
?
但是,如果我尝试这样做,我会得到:
x <- local2({
a <- 2
a * 2
})
x
## [1] 4
a
## [1] 2
所以 a
泄漏到全球环境中。这是为什么?
编辑: 更神秘:为什么它不会发生:
eval(quote({a <- 2; a*2}), new.env())
## [1] 4
a
## Error: object 'a' not found
R 函数的参数作为承诺传递。除非特别要求该值,否则不会对其进行评估。所以看看
# clean up first
if exists("a") rm(a)
f <- function(x) print(1)
f(a<-1)
# [1] 1
a
# Error: object 'a' not found
g <- function(x) print(x)
g(a<-1)
# [1] 1
a
# [1] 1
请注意,在 g()
函数中,我们使用传递给函数的值,该值是对 a
的赋值,以便在全局环境中创建 a
。使用 f()
,永远不会创建该变量,因为该函数参数仍然是承诺结束,从未被评估。
如果你想访问一个参数而不评估它,你需要使用类似 match.call()
或 subsititute()
的东西。 local()
函数执行后者。
如果删除 eval.parent()
,您会看到替换将参数中未计算的表达式放入对 eval()
.
h <- function(expr, envir = new.env()){
substitute(eval(quote(expr), envir))
}
h(a<-1)
# eval(quote(a <- 1), new.env())
好像你在做什么
j<- function(x) {
quote(x)
}
j(a<-1)
# x
您并没有真正创建新的函数调用。此外,当您 eval()
该表达式时,您会从其原始调用环境触发 x
的评估(触发承诺的评估),而不是在新环境中评估表达式。
local()
然后使用 eval.parent()
以便您可以在块内的环境中使用现有变量。例如
b<-5
local({
a <- b
a * 2
})
# [1] 10
看看这里的行为
local2 <- function(expr, envir = new.env()){
eval(quote(expr), envir)
}
local2({a<-5; a})
# [1] 5
local2({a<-5; a}, list(a=100, expr="hello"))
# [1] "hello"
看看当我们使用非空环境时,eval()
在环境中查找 expr
,而不是在环境中计算代码块。