在函数工厂r中替换正式参数

时间:2017-03-30 02:01:34

标签: r metaprogramming

我试图让函数工厂使用自定义的正式参数名称来创建函数。我们的想法是为工厂提供一个字符串,然后在返回的函数的正式和正文中替换它。我已经设法使用eval(parse(text = paste()))来完成它,但我在别处读到了这个不好的做法。如何在避免进行evalparse的同时获得相同的输出?

MyLinearRateFunctions<-function(varX){

  eval(parse(text=paste("function(a,b,",
                    varX,
                    ") 1/(a + b*",
                    varX,
                    ")",sep="")
  ))

 }

(LinearRateMPG<-MyLinearRateFunctions('mpg'))

# function(a,b,mpg) 1/(a + b*mpg)
# <environment: 0x11c2f2a00>

(LinearRateCYL<-MyLinearRateFunctions('cyl'))

# function(a,b,cyl) 1/(a + b*cyl)
# <environment: 0x11e4cb908>

(LinearRateDISP<-MyLinearRateFunctions('disp'))

# function(a,b,disp) 1/(a + b*disp)
# <environment: 0x11e47eae8>

3 个答案:

答案 0 :(得分:1)

可能有一种更简洁的方式来实现目标,但这里有一个想法:

fn <- function(x) {
    f <- eval(bquote(function(a, b) 1 / (a + b * .(as.name(x)))))
    formals(f) <- c(formals(f), setNames(alist(dummy = ), x))
    f
}

fn()的第一行,我们使用bquote()x替换为数学表达式。我们需要对其进行评估(使用eval())将其从调用转换为函数。然后在第二行中,我们将第三个参数添加到形式参数列表中。最后一行返回函数。

fn("mpg")
# function (a, b, mpg) 
# 1/(a + b * mpg)
# <environment: 0x4f05c78>

fn("cyl")
# function (a, b, cyl) 
# 1/(a + b * cyl)
# <environment: 0x4f9ba28>

快速检查:

fn("mpg")(1, 2, 3)
# [1] 0.1428571
1 / (1 + 2 * 3)
# [1] 0.1428571

感谢您提出更好的eval(parse(text = ...))替代方案,但肯定不良做法。

答案 1 :(得分:1)

以下是使用fn2 <- function(x) { fbody <- bquote(1 / (a + b * .(as.name(x)))) fargs <- setNames(alist(,,),c('a','b',x)) pryr::make_function(fargs,fbody, env=parent.frame(2)) } 的替代方案。这具有将功能“环境”设置为“功能工厂”调用

的调用环境的附加好处
eval

虽然这似乎避免了对make_function的调用,但它只是隐藏在com.microsoft.sqlserver.jdbc.SQLServerException: String or binary data would be truncated

答案 2 :(得分:1)

这仅使用基数R而不使用eval。首先设置一个用作模板的函数,然后更改形式,设置正文并设置环境。

factory <- function(x, envir = parent.frame()) {
  fun <- function(a, b, x) {}
  names(formals(fun))[[3]] <- x
  body(fun) <- substitute(1/(a+b*x), list(x = as.name(x)))
  environment(fun) <- envir
  fun
}

# test
myfun <- factory("m")

,并提供:

> myfun
function (a, b, m) 
1/(a + b * m)