在 R 中,到底是什么导致对类型名称(或符号)的对象进行求值?

In R, exactly what causes an object of type name (or symbol) to be evaluated?

运行之后:

x <- as.name("aa")
aa <- 2

在 R 中,为什么

(x)

return 2?为什么

x <- as.name("aa")
aa <- 3
get(get(x))

return3?

我知道 get() 需要一个字符串,但我不明白为什么它不计算 x,在其中找到字符串,然后获取它。在我看来,有时函数会对其参数进行此类评估,有时则不会。例如,在第二个示例中,如果将 get(get(x)) 替换为 eval(x),则 eval() 计算 x 以找到名称,然后计算名称以找到 3.

因为x的值不是2,而是符号(或名称)aa。但是,如果您 eval 它:

> eval(x)
[1] 2

同样,get(x) 根本不起作用(即产生错误),因为根据 get 的文档,它的第一个参数必须是 an object name (given as a character string),其中括号旨在将其与 symbol/name.

区分开来

get 仅适用于字符参数:

 > get("aa")
[1] 2

symbol(我发现它比 name 更容易混淆)不是一回事:

> identical("aa",as.name("aa"))
[1] FALSE

as.nameas.symbol 做同样的事情。)

关于 "evaluation of expressions" 与 "evaluation of function arguments" 区别的精彩解释,请参阅@MrFlick 的回答。

我认为@joran 的回答是正确的,但也许我可以尝试用不同的方式解释。

R中的("function"本质上就是恒等函数。它回显你传递给它的内容。它几乎就像它不存在一样。这些语句

将 return 编辑的内容没有区别
x      #1
(x)    #2
((x))  #3

括号只是传递里面的值。您可以根据需要添加任意数量的括号,它不会更改 returned 的内容。评估者查看 ((x)),查看外括号,并且只知道 return 括号内的值。所以现在它只解析 (x),再次,它看到外括号并且只 return 括号内的值,即 x。括号只是从内部传递值;他们不评价它。

裸值x是一个名称(或符号)。名称并不唯一地绑定到值。名称和值之间的映射因环境而异。这就是为什么必须在特定上下文中评估名称才能获得值的原因。考虑这些例子

aa <- 5
dd <- data.frame(aa=20)
x <- as.name("aa")
foo <- function(x) {aa<-10; eval(x)}

eval(x)
# [1] 5
foo(x)
# [1] 10
eval(x, dd)
# [1] 20

这种行为实际上是非常可取的。这就是使需要非标准评估工作的功能的原因,例如

subset(mtcars, hp<100)

当您使用 R 控制台时,它的行为与 REPL 相同——它会读取您的输入、对其求值、打印它,然后等待下一个输入。请注意,它只进行一级评估,并且评估发生在 "current" 环境中。它不会递归地评估表达式中的 returned 值。所以当你这样做时

x <- as.name("aa")
x   # identical to (x)
# aa

当 REPL 进入评估步骤时,它评估指向名称 aa 的名称 x。而已。一级评价。名称 aa 随后未被评估。

?eval 帮助页面中有一条说明是这样说的:

eval evaluates its first argument in the current scope before passing it to the evaluator

那里没有 "double" 评估。它只是评估它的参数,就像 R 中的任何其他函数一样。例如

aa <- 5 
bar <- function(x) print(x)
bar(aa+2)
# [1] 7

它打印“7”,而不是 "aa+2",因为该函数在打印之前已经评估了它的参数。也解释了这两者的区别

dd <- data.frame(bb=20)
xx <- as.name("bb")
eval(bb, dd)
# Error in eval(bb, dd) : object 'bb' not found
eval(xx, dd)
# [1] 20

在第一个 eval() 调用中,R 无法在当前环境中计算 bb,因此出现错误。但请注意

evalq(bb, dd)

有效,因为 evalq 不会尝试计算第一个表达式参数。