0

我试图了解 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)))
}

据我了解,会发生两轮表达式引用和后续评估:

  1. eval(quote([whatever expr input]), [whatever envir input])由 生成为未评估的调用substitute
  2. 调用在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
4

1 回答 1

2

R 函数的参数作为 Promise 传递。除非特别要求该值,否则不会对它们进行评估。所以看看

# 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(),永远不会创建该变量,因为该函数参数仍然是一个承诺 end 从未评估过。

如果你想访问一个参数而不评估它,你需要使用类似match.call()or的东西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的,而不是在环境中评估您的代码块。

于 2017-11-22T19:30:09.447 回答