应用于相同的R代码或对象时,引用和替换通常返回不同的对象。怎样才能使这种区别显而易见?
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
> tmc <- function(X){
out <- list(typ = typeof(X), mod = mode(X), cls = class(X))
out
}
> df1 <- data.frame(a = 1, b = 2)
这里报价和替代品的打印输出是相同的。
> quote(df1)
df1
> substitute(df1)
df1
两者的结构相同。
> str(quote(df1))
symbol df1
> str(substitute(df1))
symbol df1
并且类型,模式和类都相同。
> tmc(quote(df1))
$typ
[1] "symbol"
$mod
[1] "name"
$cls
[1] "name"
> tmc(substitute(df1))
$typ
[1] "symbol"
$mod
[1] "name"
$cls
[1] "name"
但是,输出不相同。
> is.identical(df1)
[1] FALSE
请注意,this question显示了一些输入,这些输入使两个功能显示不同的输出。但是,即使它们看起来相同,输出也不同,并且在大多数通常的测试中它们都是相同的,如上述is.identical()的输出所示。这种看不见的区别是什么?如何使它显现?
在标签上注意:我猜通用LISP引号和R引号相似
答案 0 :(得分:4)
原因是substitute()
的行为因调用位置而不同,或更确切地说,取决于调用的内容。
要了解会发生什么,需要非常仔细地解析substitute()
的(细微)文档,尤其是:
通过检查解析树的每个组件来进行替换 如下所示:如果它不是env中的绑定符号,则它保持不变。如果它 是一个promise对象,即函数或函数的形式参数 使用delayAssign()显式创建的 许诺替换符号。如果是普通变量,则其值为 被替换,除非env为.GlobalEnv,在这种情况下,符号为 保持不变。
所以实际上有三个选择。
在这种情况下:
> df1 <- data.frame(a = 1, b = 2)
> identical(quote(df1),substitute(df1))
[1] TRUE
df1
是一个“普通变量”,但是在.GlobalEnv
中被调用,因为env
参数默认为当前评估环境。因此,在最后一种情况下,符号df1
保持不变,因此与quote(df1)
的结果相同。
在函数的上下文中:
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
重要的区别在于,现在我们在X
而不是df1
上调用这些函数。对于大多数R用户来说,这是一个愚蠢的琐碎区别,但是当使用诸如substitute
之类的微妙工具时,它变得很重要。 X
是函数的形式参数,因此这意味着我们处于所记录行为的不同情况下。
具体地说,它说现在“ promise的表达方式代替符号”。我们可以了解如果debug()
函数并在函数环境的上下文中检查对象,这意味着什么:
> debugonce(is.identical)
> is.identical(X = df1)
debugging in: is.identical(X = df1)
debug at #1: {
out <- identical(quote(X), substitute(X))
out
}
Browse[2]>
debug at #2: out <- identical(quote(X), substitute(X))
Browse[2]> str(quote(X))
symbol X
Browse[2]> str(substitute(X))
symbol df1
Browse[2]> Q
现在我们可以看到发生的事情恰恰是文档所说的事情(哈!太明显了!;))
X
是形式参数或诺言,根据R,它与df1
是不同。对于大多数编写函数的人来说,它们实际上是相同的,但是内部实现却不同。 X
是一个承诺对象,substitute
用它“指向”的符号X
替换符号df1
。这就是文档“承诺的表达方式”的意思;这就是R在函数调用的X = df1
部分中看到的内容。
为解决问题,尝试猜测在这种情况下会发生什么:
is.identical <- function(X){
out <- identical(quote(A), substitute(A))
out
}
is.identical(X = df1)
(提示:现在A
并不是“环境中的绑定符号”。)
最后一个示例更直接地说明了文档中的最终情况,但带有令人困惑的异常:
#Ordinary variable, but in .GlobalEnv
> a <- 2
> substitute(a)
a
#Ordinary variable, but NOT in .GlobalEnv
> e <- new.env()
> e$a <- 2
> substitute(a,env = e)
[1] 2