Hadley的《高级R》书中的非标准评估

8
在 Hadley 的 Advanced R 书中,有一段代码的输出我无法理解。
f <- function(x) substitute(x)
g <- function(x) deparse(f(x))
g(1:10)
g(x)
g(x + y ^ 2 / z + exp(a * sin(b)))

为什么它们都会返回 "x"?特别是当:
g <- function(x) deparse(substitute(x))

按预期返回"1:10""x""x + y ^ 2 / z + exp(a * sin(b))"

2个回答

11

首先,一些背景信息:承诺是一个未求值的参数。一个承诺包括两个部分:1)产生这个延迟计算的代码/表达式(可以通过substitutepryr::promise_info查看该代码),以及2)创建和应该在其中评估该代码/表达式的环境(可以通过pryr::promise_info查看该环境)。

如果将g()函数更改为,则问题也更加清晰

g <- function(x) deparse(f(whatever))

你总是会得到 "whatever"。这是因为当 g() 调用 f(whatever) 时,它向 f() 传递了一个 Promise 对象--该对象具有代码 whateverg() 执行环境的环境。然后,在 f() 中的 substitute 查看这个 Promise 对象并返回该 Promise 的代码/表达式,即在本例中为 whatever

可以通过运行以下代码来确认 Promise 对象的代码和环境:

library(pryr) # need to install it
f <- function(x) {
  print(promise_info(x))
  substitute(x)
}

g <- function(x) {
  print(environment())
  f(whatever)
}
g(1:10)

底线是你会得到你传递给f(whatever)的任何内容。这就是为什么将这些函数分开不是一个好主意。其中一个解决方法是使用

g <- function(...) deparse(f(...))

这种方式将参数传递给f(),并且不会在g()中重命名。

另一方面,g <- function(x) deparse(substitute(x)); g(1:10)会产生1:10,因为在这种情况下,substitute查看的是承诺对象x(与上述情况中的承诺对象whatever相反)。 承诺x在这里具有代码1:10和环境R_GlobalEnv。(同样,可以使用g <- function(x) { print(promise_info(x) ; deparse(substitute(x))进行检查。 因此,substitute(x)按预期返回1:10


但这并不能解释为什么 deparse(substitute(x)) 给出了正确的响应。在这种情况下,g() 调用了 substitute(x),因此当 substitute() 寻找传递给它的表达式时,它总是看到 x。为什么不是这样呢? - Heisenberg
deparse(substitute(x)) 之所以有效,是因为你没有第二级的重定向。在 f()g() 中的 x 是不同的。实际上,substitute 是在传递给函数的 promise 对象上工作的。一旦你调用另一个函数,那个 promise 就必须被解析。因此,如果你在同一个函数调用中完成它,就不会有这个问题。 - MrFlick
“实际上,substitute 函数是在处理传递给它的 Promise 对象。”你所指的 Promise 对象和函数是什么?(只是澄清) - Heisenberg
从文档中可以看到:“如果它是一个promise对象,即作为函数的形式参数或使用delayedAssign()显式创建的对象,则该promise的表达式槽将替换符号。”您能否澄清在这种情况下表达式槽和符号是什么? - Heisenberg
@Heisenberg 这与惰性求值有关。参数作为未求值的表达式传递,或更正式地说,是承诺。如果您从未请求该值,则实际上不会传递或创建它。使用 deparse(substitute(x)),符号是 x,表达式槽包含您实际传递给函数的内容,例如 1:10 - MrFlick

0

我也曾经为此苦恼,以下是我对于清晰解释的理解:

f <- function(x) substitute(x)

g <- function(x) deparse(f(x))

g(5)返回“x”。为什么?

首先,deparse没有任何区别。

f <- function(x) substitute(x)

g <- function(x) (f(x))

g(5) 会返回 x

当我们执行 g(5) 时会发生什么?

首先,使用参数 x = 5 调用函数 g。创建一个执行环境来执行函数 () - 或 deparse - 并将全局环境作为其父级。在此执行环境中,x = 5。基本上,5 被传递给函数 ()。

然后,在 g 的执行环境之外调用函数 f,即 substitute。创建 f 的执行环境,并将 g 的执行环境作为其父级。传递了一个参数 x。请注意,这个 x 只是一个符号,表示将参数传递给函数。它尚未被评估。

在 f 的执行环境中,此参数未被评估,因为那里只有一个函数 substitute(),根据定义不进行评估。对于任何正常的函数,例如 sqrt(x),需要评估 x。在 f 的执行环境中,找不到这个 x,R 将向上查找到 g 的执行环境。在那里,x 将被找到并且将取出 x 的平方根并返回。

您必须阅读 Hackley 的书中的“环境”章节才能理解这一点。

例如,运行以下内容以查看传递的参数和环境:

## g <- function(x)(f(whatever))
f <- function(x) {
print("----------")
print("starting f, substitute")
print("promise info:") 
print(promise_info(x))
print("current env:")
print(environment()) 
print("function was called in env:")
print(parent.frame())
print("now do subst") 

substitute(x)
}

g <- function(x) {
print("--------")
print("##g <- function(x)(f(x))")
print("-----")
print("starting g ")
print("promise info:")
## A promises comprises of two parts: 1) the code / expression that gives rise to this delayed computation and
## 2) the environment where this code / expression is created and should be evaluated in.
print(promise_info(x))  
print("current env:")
print(environment())
print("function was called in env:")
print(parent.frame())
print("now do f(x)")
f(x)
}

当调用g时,将5作为参数传递。 当调用f时,x作为参数传递(这个x永远不会被substitute()评估,它被称为“promise”,只有在需要时才会被评估)

现在,考虑 h <- function(y) deparse(substitute(y))

运行:

h <- function(y) {
print("----------")
print("## h <- function(y) deparse(substitute(y))")
print("-----")
print("starting h")
print("promise info:") 
print(promise_info(y))
print("current env:")
print(environment()) 
print("function was called in env:")
print(parent.frame())
deparse(substitute(y))
}

只有一个阶段,为deparse(substitute())创建了执行环境,并将5作为参数传递给它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接