来自:
http://www.biosino.org/R/R-doc/R-lang/Substitutions.html#Substitutions
事实上,向上节内容中提到的一样,修改一个表达式内部结构是很少见的。 最常见的是,用户简单地想得到一个表达式以分析它并且用它 来作标记图形一类的事情。这样的一个例子可见于 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(loge)。
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)宏。