为什么替换函数的参数不会被懒惰地评估?

Fer*_*aft 15 r promise

考虑以下简单功能:

f <- function(x, value){print(x);print(substitute(value))}
Run Code Online (Sandbox Code Playgroud)

争论x最终会被评估print,但value绝不会.所以我们可以得到这样的结果:

> f(a, a)  
Error in print(x) : object 'a' not found  
> f(3, a)  
[1] 3  
a  
> f(1+1, 1+1)  
[1] 2  
1 + 1  
> f(1+1, 1+"one")  
[1] 2  
1 + "one"
Run Code Online (Sandbox Code Playgroud)

一切都如预期的那样.

现在考虑替换函数中的相同函数体:

'g<-' <- function(x, value){print(x);print(substitute(value))}
Run Code Online (Sandbox Code Playgroud)

(单引号应该是花哨的引号)

我们来试试吧:

> x <- 3  
> g(x) <- 4  
[1] 3  
[1] 4  
Run Code Online (Sandbox Code Playgroud)

到目前为止没什么异常的......

> g(x) <- a  
Error: object 'a' not found  
Run Code Online (Sandbox Code Playgroud)

这是出乎意料的.名称a应打印为语言对象.

> g(x) <- 1+1  
[1] 4  
1 + 1  
Run Code Online (Sandbox Code Playgroud)

这是好的,因为x以前的价值是4.请注意表达式未经评估.

最后的测试:

> g(x) <- 1+"one"  
Error in 1 + "one" : non-numeric argument to binary operator  
Run Code Online (Sandbox Code Playgroud)

等一下......为什么要尝试评估这个表达式?

那么问题是:错误或功能?这里发生了什么?我希望一些大师用户会对R上的承诺和懒惰评估有所了解.或者我们可能只是认为它是一个错误.

had*_*ley 12

我们可以将问题简化为一个稍微简单的例子:

g <- function(x, value)
'g<-' <- function(x, value) x
x <- 3

# Works
g(x, a)
`g<-`(x, a)

# Fails
g(x) <- a
Run Code Online (Sandbox Code Playgroud)

这表明R在评估替换函数时正在做一些特殊的事情:我怀疑它会评估所有参数.我不知道为什么,但在C代码(注释https://github.com/wch/r-source/blob/trunk/src/main/eval.c#L1656https://github.com /wch/r-source/blob/trunk/src/main/eval.c#L1181)建议可能是为了确保不会意外修改其他中间变量.

Luke Tierney对当前方法的缺点进行了长时间的评论,并说明了可以使用替换函数的一些更复杂的方法:

这里的方法有两个问题:

复杂赋值中的复杂赋值 f(x, y[] <- 1) <- 3可能会导致外部赋值的值临时变量被覆盖,然后被内部赋值删除.这可以通过使用多个临时值或使用对该变量的承诺来解决,如对RHS所做的那样.然后可能需要调整在错误消息中打印替换函数调用.

对于表单f(g(x, z), y) <- w的赋值,z将计算两次的值,一次用于调用g(x, z) ,一次用于调用替换函数g<-.有可能通过使用promises来解决这个问题.使用更多的临时工作将无法工作,因为它会破坏使用替代和/或非标准评估的替换函数(并且有一些软件包可以做到这一点 - igraph是一个).


Jos*_*ien 8

我认为关键可以在第1682行"eval.c"开始的评论中找到(并且紧接着是对赋值操作的RHS的评估):

/* It's important that the rhs get evaluated first because
assignment is right associative i.e. a <- b <- c is parsed as
a <- (b <- c). */

PROTECT(saverhs = rhs = eval(CADR(args), rho));
Run Code Online (Sandbox Code Playgroud)

我们预计,如果我们这样做g(x) <- a <- b <- 4 + 5,既ab将被分配的值9; 这实际上是会发生什么.

显然,R 确保这种一致行为的方式是在执行其余任务之前始终首先评估任务的RHS.如果该评估失败(如同您尝试的那样g(x) <- 1 + "a"),则会抛出错误并且不会进行任何分配.