你可以使用substitute()从文本字符串在R中创建一个函数吗?

问题描述

我正在尝试编写一个函数,允许我输入 fieldoperatorvalue 的文本字符串,并返回一个简单的 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”的方式是使用 formalsbody

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\""

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...