expr(mean(1:10))和expr(mean(!!(1:10))之间的区别

时间:2018-12-31 07:27:15

标签: r metaprogramming

在Hadley的《 Advanced R 2nd ed》一书的元编程部分中,我很难理解这个概念。我已经用R编程了一段时间,但这是我第一次遇到元编程的概念。这个练习题尤其使我感到困惑

“以下两个调用打印相同,但实际上不同:

(a <- expr(mean(1:10)))
#> mean(1:10)
(b <- expr(mean(!!(1:10))))
#> mean(1:10)
identical(a, b)
#> [1] FALSE

有什么区别?哪一个更自然?”

当我评估它们时,它们都返回相同的结果

> eval(a)
[1] 5.5
> eval(b)
[1] 5.5

当我在a和b对象内部查看时,第二个对象的打印确实不同,但是我不确定这两个对象之间的区别是什么

> a[[2]]
1:10
> b[[2]]
[1]  1  2  3  4  5  6  7  8  9 10

如果我只是在没有eval(expr(...))的情况下运行它们,那么它将返回不同的结果:

mean(1:10)
[1] 5.5
mean(!!(1:10))
[1] 1

我的猜测是,没有expr(...)!!(1:10)会起到双重否定的作用,在强制作用下,强制所有数字均为1,因此均值为1。

我的问题是:

  1. 为什么!有和没有expr(...)的行为都不同?我希望eval(expr(mean(!!(1:10))))返回的结果与mean(!!(1:10))相同,但这不是

  2. 我仍然不太完全了解一个对象和b个对象之间的区别是什么?

先谢谢您

3 个答案:

答案 0 :(得分:0)

这是区别。当我们取反(!)整数向量时,非0的数字将转换为FALSE,而0将转换为TRUE。与另一个否定即。 double(!!),则将FALSE更改为TRUE,反之亦然

!0:5
#[1]  TRUE FALSE FALSE FALSE FALSE FALSE

!!0:5
#[1] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE

以OP的示例为准,TRUE

!!1:10
#[1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE

和TRUE / FALSE可以为1/0

as.integer(!!1:10)
#[1] 1 1 1 1 1 1 1 1 1 1

因此mean为1

mean(!!1:10)
#[1] 1

关于'a'与'b'

str(a)
#language mean(1:10)
str(b)
#language mean(1:10)

两者都是语言对象,将对其进行评估以获得数字1:10的mean

all.equal(a, b)
#[1] TRUE

如果我们需要获得10个数字的mean,则第一个是正确的方法。

我们可以eval正确地设置第二个选项,即通过mean设置

来获得quote的值1。
eval(quote(mean(!!(1:10))))
#[1] 1
eval(quote(mean(1:10)))
#[1] 5.5

答案 1 :(得分:0)

!!在这里不是双重否定,而是rlang中的unquote运算符。

  

取消引用是引用的一种逆向。它允许您有选择地   评估expr()中的代码,使expr(!! x)等效于x。

ab之间的区别是,该参数在a中作为未评估的调用保留在b中:

class(a[[2]])
[1] "call"
class(b[[2]])
[1] "integer"

a行为在某些情况下可能是有利的,因为它会延迟评估,或者由于相同的原因而不利。如果这是一个缺点,那就是造成极大挫败感的原因。如果参数是一个较大的向量,则b的大小将增加,而a的大小将保持不变。

有关更多详细信息,请参见section 19.4 of Advanced R

答案 2 :(得分:0)

!!expr 中使用时有特殊含义。

expr 之外你会得到不同的结果,因为 !! 是双重否定

即使在 expr 内部,这两个版本也是不同的,因为 1:10 是一个产生整数向量的表达式 评估时,而 !!(1:10) 是 计算相同的表达式。

一个表达式及其求值后的结果是 不同的东西。

enter image description here

相关问题