应用系列功能的范围如何?

问题描述

考虑:

x <- 5
replicate(10,x <- x + 1)

这有输出 c(6,6,6)。但是:

x <- 5
replicate(10,x <<- x + 1)

输出 c(6,7,8,9,10,11,12,13,14,15)

这对评估 x <- x + 1 的环境意味着什么?我是否相信 x 被视为 replicate 的内部变量?这似乎是我所看到的,但是当我咨询 the relevant section of the language definition 时,我看到了以下内容

还值得注意的是,如果对参数进行求值,则 foo(x

但是如果 x 真的在调用环境中改变了,那为什么会:

x <- 5
replicate(10,x <- x + 1)
x

返回 5 而不是 15?我误解了哪一部分?

解决方法

您从语言定义中引用的句子是关于标准评估的,但 replicate 使用的是非标准评估。这是它的来源:

replicate <- function (n,expr,simplify = "array") 
sapply(integer(n),eval.parent(substitute(function(...) expr)),simplify = simplify)

substitute(function(...) expr) 调用接受您的表达式 x <- x + 1 而不计算它,并创建一个新函数

function(...) x <- x + 1

这是传递给 sapply() 的函数,它将其应用于长度为 n 的向量。所以所有的赋值都在那个匿名函数的框架中进行。

当您使用 x <<- x + 1 时,计算仍然发生在构造函数中,但它的环境是 replicate() 的调用环境(因为 eval.parent 调用),这就是分配发生。这就是您在输出中获得递增值的原因。

所以我认为你正确理解了手册,但它没有明确说明它在谈论标准评估的情况。以下段落暗示了这里发生的事情:

可以访问用作函数内部参数的实际(非默认)表达式。该机制是通过承诺实现的。当一个函数被求值时,用作参数的实际表达式与一个指向函数被调用的环境的指针一起存储在promise中。当 (if) 参数被评估时,存储的表达式在调用函数的环境中被评估。由于仅使用指向环境的指针,因此在此评估期间对该环境所做的任何更改都将生效。然后,结果值也存储在 Promise 中的一个单独位置。后续评估会检索此存储值(不执行第二次评估)。也可以使用替换来访问未计算的表达式。

但是 replicate() 的帮助页面没有明确说明这是它在做什么。

顺便说一句,您的标题询问了 apply family 函数:但是除了 replicate 之外的大多数函数都明确要求使用函数,因此这里不会出现此问题。例如,很明显这不会影响全局 x:

sapply(integer(10),function(i) x <- x + 1)