如何使 quote() 和 substitute() 的输出差异可见?

How can one make visible the difference in the outputs of quote() and substitute()?

应用于相同的 R 代码或对象时,通常引用和替换 return 不同的对象。如何才能使这种差异变得明显?

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和substitute的打印输出是一样的

> quote(df1)
df1
> substitute(df1)
df1

而且两者的结构是一样的

> str(quote(df1))
 symbol df1
> str(substitute(df1))
 symbol df1

而且类型、方式和class都一样

> 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

请注意, 显示了一些导致两个函数显示不同输出的输入。然而,即使它们看起来相同,输出也是不同的,并且大多数常用测试都是相同的,如上面 is.identical() 的输出所示。这种看不见的区别是什么,我怎样才能让它显现出来?

标签注意事项:我猜 Common LISP 引用和 R 引用很相似

原因是 substitute() 的行为因调用位置的不同而不同,或者更准确地说,调用的对象不同。

了解会发生什么需要非常仔细地分析 substitute() 的(微妙的)文档,具体来说:

Substitution takes place by examining each component of the parse tree as follows: If it is not a bound symbol in env, it is unchanged. If it is a promise object, i.e., a formal argument to a function or explicitly created using delayedAssign(), the expression slot of the promise replaces the symbol. If it is an ordinary variable, its value is substituted, unless env is .GlobalEnv in which case the symbol is left unchanged.

所以基本上有三个选项。

在这种情况下:

> df1 <- data.frame(a = 1, b = 2)
> identical(quote(df1),substitute(df1))
[1] TRUE

df1 是一个 "ordinary variable", 它在 .GlobalEnv 中被调用,因为 env 参数默认为当前评价环境。因此,我们处于符号 df1 保持不变的最后一种情况,因此它与 quote(df1).

的结果相同

在函数上下文中:

is.identical <- function(X){
    out <- identical(quote(X), substitute(X))
    out
}

重要的区别是现在我们在 X 而不是 df1 上调用这些函数。对于大多数 R 用户来说,这是一个愚蠢、微不足道的区别,但是当使用像 substitute 这样的微妙工具时,它就变得很重要。 X 是函数的形式参数,因此这意味着我们处于记录行为的 不同 案例中。

具体地说,现在 "the expression slot of the promise replaces the symbol"。如果我们 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是一个promise对象,substitute将符号X替换为"points to",即df1。这就是文档中 "expression slot of the promise" 的意思;这就是 R 在函数调用的 X = df1 部分看到的内容。

为了解决问题,请尝试猜测在这种情况下会发生什么:

is.identical <- function(X){
    out <- identical(quote(A), substitute(A))
    out
}

is.identical(X = df1)

(提示:现在 A 不是 "bound symbol in the environment"。)

最后一个例子更直接地说明了文档中的最后一个案例,但有一个令人困惑的异常:

#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