R语言元编程基础函数
所谓的元编程(metaprogramming)其实就是编写运行时动态修改程序本身的代码(编写产生代码的代码)。 R语言支持元编程,有几个基础函数需要深入了解:
substitute
替换函数。形式substitute(expr, env)
,表达式expr中的变量使用env中的绑定的变量的值(若是函数参数,因为是promise的缘故还没有eval)替换,env不进行回溯,只在当前或指定的env中搜索。而且env不能是.GlobalEnv,如果是.GlobalEnv,不进行替换。env可以是list。
A promise捕获(capture)了需要计算的表达式(但是没有计算eval),以及计算表达式所处的environment. 第一次访问promise时,触发表达式的计算,从而产生environment中的表达式对应绑定对象的值。promise就像“薛定谔的猫”,一旦访问,就触发计算,从而退出promise状态。
a <- 1
b <- 11
res1 <- substitute(x + y, list(x = sin(3), y = a/b))
# 0.141120008059867 + 0.0909090909090909
c(class(res1), typeof(res1))
# [1] "call" "language"
sf <- function(x, y) {
substitute(x + y)
}
res2 <- sf(sin(10), a/b)
# sin(10) + a/b
c(class(res2), typeof(res2))
# [1] "call" "language"
substitute不仅变量能够替换,运算符(函数)也能够替换:
> substitute(a + b, list("+" = quote(func)))
func(a, b)
quote
quote(expr)
简单地返回expr,expr没有进行eval。和substitute相比,没有替换。
res3 <- quote(a/b)
# a/b
c(class(res3), typeof(res3))
# [1] "call" "language"
subtitute/quote返回class/type根据表达式的不同而不同。表达式被解析为抽象语法树,可以访问其中每一个组成,还可以对返回的对象进行修改,即自己构造语法树(meta-programming)。
eval
eval(expr, envir = parent.frame(),
enclos = if(is.list(envir) || is.pairlist(envir))
parent.frame() else baseenv())
对expr进行计算,默认是当前环境(也就是eval的parent.frame)。如果envir是list/data.frame,encloss参数默认增加当前环境,作为expr计算在list/data.frame之外寻找对象定义的闭包。
> eval(res1)
[1] 0.2320291
> eval(res2)
[1] -0.453112
> eval(res3)
[1] 0.09090909
deparse
deparse(expr)
deparse把没有eval的表达式转换成字符串。
> deparse(res1)
[1] "0.141120008059867 + 0.0909090909090909"
> deparse(res2)
[1] "sin(10) + a/b"
> deparse(res3)
[1] "a/b"
call
call(name, ...)
构造一个没有eval的函数调用。第一个参数是函数名(字符串),后面是函数的参数。
举个栗子:
> call_x <- call("+", 7, 8)
> class(call_x)
[1] "call"
> typeof(call_x)
[1] "language"
> eval(call_x)
[1] 15
> deparse(call_x)
[1] "7 + 8"
expressioin
expression(...)
把参数看成表达式,返回expression类型的对象。expression类型和list类型类似,是个容器,每个成员可能是call,symbol(name)和constants中的一种。
若有多个成员,eval每个成员都会求值一遍,但是只返回最后一个成员eval之后的值,这与绝大多数编程语言多个表达式语句(逗号隔开)返回的结果类似。
举个栗子:
> a <- 1
> b <- 2
> c <- 3
> ex1 <- expression(a, 1:10, c <- 10, FALSE)
> sapply(ex1, class)
[1] "name" "call" "<-" "logical"
> sapply(ex1, typeof)
[1] "symbol" "language" "language" "logical"
> eval(ex1)
[1] FALSE
> c
[1] 10
parse
parse(text="")
是deparse的逆函数,把表达式字符串解析成为未eval的expression。
> ex1 <- parse(text = "1 + 2 + 3")
> ex1
expression(1 + 2 + 3)
> eval(ex1)
[1] 6
最后一幅图总结这些函数的关系: