Next: More on evaluation, Previous: Direct manipulation of language objects, Up: Computing on the language
事实上,向上节内容中提到的一样,修改一个表达式内部结构是很少见的。
最常见的是,用户简单地想得到一个表达式以分析它并且用它
来作标记图形一类的事情。这样的一个例子可见于
plot.default 实现代码的起始部分:
xlabel <- if (!missing(x))
deparse(substitute(x))
这使得 plot的x参数变量或表达式随后可
用来标记x-轴。
实现这一要求的是函数 substitute。它获得表达式
x 并且替换通过形式参数 x 传递的表达式。
注意,为了保证这样运行,
x 必须拥有产生它的值的表达式的
信息。这和 R 的悠闲求值架构有关(see Promise objects)。
一个形式参数事实上是一个允诺,该对象有三个槽变量,
一个用于定义它的表达式,一个用于表达式求值的环境,
还有一个用于表达式的求值结果。
substitute 识别允诺变量并且替换它的表达式槽变量的值。
如果 substitute 在一个函数内部被调用,
该函数的局部变量也受替换支配。
substitute 的参数没有必要是一个简单的标识符,它可以
是一个含有多个变量的表达式。此时,任何一个变量都会发生替换。
同样,substitute 有一个额外的参数,它是一个
变量可以搜索的环境或者列表。例如:
> substitute(a + b, list(a = 1, b = quote(x)))
1 + x
注意,引用(quoting)是x替换所必需的。这种构造方便
在图中增加数学表达式,如下面的代码所示
> plot(0)
> for (i in 1:4)
+ text(1, 0.2 * i,
+ substitute(x[ix] == y, list(ix = i, y = pnorm(i))))
值得注意的是替换是纯词法上实现的;
如果它们被求值了,则不会对结果调用对象的意义进行检验。
substitute(x <- x + 1, list(x = 2)) 会恰当地返回
2 <- 2 + 1。但是, R的有些部分自己定义了什么是有意义
和什么无意义的规则,而且事实上就是采用了这些形式上有问题的表达式。
例如,使用“图中数学”的特性时常常会有语法上正确,但求值毫无意义的
构造,如 {}>=40*" years"。
替换不会对第一个参数求值。这导致如何替换包含在一个变量中的对象的
问题。 解决问题的方法是再用一次 substitute ,如下所示
> expr <- quote(x + y)
> substitute(substitute(e, list(x = 3)), list(e = expr))
substitute(x + y, list(x = 3))
> eval(substitute(substitute(e, list(x = 3)), list(e = expr)))
3 + y
替换的精确规则如下:
第一个参数的解析树的每个符号
和第二个参数匹配,既可以是有标签的列表也可以是环境框架。如果它
是一个简单的局部对象,它的值将被插入, 除非它
匹配全局变量。如果它是一个允诺(常常是函数参数),
允诺表达式会被替换。如果符号没有被匹配,它不会有任何改变。
而在最高层次的替换很少有例外的。1
这是从 S 继承而来,原理基本上是变量可能在那个层次上绑定
使得替换最好和quote类似而且没有控制。2
如果局部变量在substitute使用前被替换,允诺替换的规则和
S相应的规则稍稍有点不同。
R 将使用变量的新值,而 S 将无条件地使用参数表达式 —
除非它是一个常量。这导致一个很古怪的结果,即在S里面 f((1)) 可能和
f(1) 差异很大。但 R 的规则相当地清晰,尽管
它也有一些比较奇怪的和悠闲求值相关结果。
参看下面的例子
logplot <- function(y, ylab = deparse(substitute(y))) {
y <- log(y)
plot(y, ylab = ylab)
}
这看上去比较直接,但是 y 标签变成了一个比较难看的 c(...) 表达式。
这是由于悠闲求值的规则导致在y修改后
ylab表达式的求值。 解决方法是首先强制 ylab求值,即:
logplot <- function(y, ylab = deparse(substitute(y))) {
ylab
y <- log(y)
plot(y, ylab = ylab)
}
注意,这种情况下,eval(ylab)可能很少使用。如果
ylab 是一个语言或表达式对象,那么这将导致
这些对象也被求值。但有时结果不是期望的,如传递的数学表达式
是quote(log[e](y))。
substitute 的一个变种是 bquote,它把一些子
表达式的值代替它们自己。
上面的例子可以如下
> plot(0)
> for (i in 1:4)
+ text(1, 0.2 * i,
+ substitute(x[ix] == y, list(ix = i, y = pnorm(i))))
也可以更简洁的写成
plot(0)
for(i in 1:4)
text(1, 0.2*i, bquote( x[.(i)] == .(pnorm(i)) ))
除了.()子表达式的内容被它们的值替换外,其它表达式都被引用。
有一个可选的参数计算其它不同环境中的值。
bquote的语法源自 LISP 的后置引用(backquote)宏。
[1] 译者注: 原文为:“The special exception for substituting at the top level is admittedly peculiar. ”
[2] 译者注:
原文为:“ It has been inherited from S and the
rationale is most likely that there is no control over which variables
might be bound at that level so that it would be better to just make
substitute act as quote. ”