问题描述
我正在尝试编写一个函数,允许我输入 field
、operator
和 value
的文本字符串,并返回一个简单的 dplyr::filter
函数,我然后可以应用于数据集。
示例:
library(dplyr)
field <- "Species"
operator <- "=="
value <- "virginica"
myfun <- substitute(
function(x) filter(x,EXPR(FIELD,VALUE)),list(
FIELD = as.symbol(field),EXPR = as.symbol(operator),VALUE = value
)
)
myfun
function(x) filter(x,Species == "virginica")
到目前为止,一切都很好,对吧?看起来我们都准备好了。但没那么快:
> myfun(iris)
Error in myfun(iris) (from foo.R!10Zf0E#19) : Could not find function "myfun"
如果我输入 class(myfun)
,结果是我创建了一个叫做 call
的东西。但我真的想要一个函数。有没有办法把调用变成一个函数,或者重写上面的代码,这样我实际上最终得到了一个工作函数?
解决方法
嗯,这很容易。解决方案是用 substitute()
包裹 eval()
,如下所示:
> myfun <- eval(
substitute(
function(x) filter(x,EXPR(FIELD,VALUE)),list(
FIELD = as.symbol(field),EXPR = as.symbol(operator),VALUE = value
)
)
)
然后 myfun(iris)
按预期工作。
更“R”的方式是使用 formals
和 body
f <- function() {}
formals(f) <- alist(x = )
body(f) <- substitute(
filter(x,list(FIELD = as.symbol(field),VALUE = value)
)
f
# function (x)
# filter(x,Species == "virginica")
f(iris)
此外,您还可以在函数中添加形式和线条:
formals(f) <- c(formals(f),alist(y = 120))
f
# function (x,y = 120)
# filter(x,Species == "virginica")
body(f) <- as.call(c(as.name('{'),quote(x <- head(x,y)),body(f)))
f
# function (x,y = 120)
# {
# x <- head(x,y)
# filter(x,Species == "virginica")
# }
f(iris)
还编辑函数的行:
body(f)[[2]] <- quote(x <- tail(x,y))
# function (x,y = 120)
# {
# x <- tail(x,Species == "virginica")
# }
f(iris)
,
我们可以动态创建过滤器表达式并使用 eval(parse())
或 eval(parse_expr())
。
myfun <- function(x,field,operator,value) {
dplyr::filter(x,eval(rlang::parse_expr(sprintf('%s %s "%s"',value))))
}
field <- "Species"
operator <- "=="
value <- "virginica"
myfun(iris,value)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1 6.3 3.3 6.0 2.5 virginica
#2 5.8 2.7 5.1 1.9 virginica
#3 7.1 3.0 5.9 2.1 virginica
#4 6.3 2.9 5.6 1.8 virginica
#5 6.5 3.0 5.8 2.2 virginica
#6 7.6 3.0 6.6 2.1 virginica
#7 4.9 2.5 4.5 1.7 virginica
#8 7.3 2.9 6.3 1.8 virginica
#9 6.7 2.5 5.8 1.8 virginica
#10 7.2 3.6 6.1 2.5 virginica
#...
#...
哪里
sprintf('%s %s "%s"',value)
#[1] "Species == \"virginica\""