在函数工厂 r 中替换形式参数
Substitute formal arguments in a function factory r
我正在尝试创建一个函数工厂来创建具有自定义形式参数名称的函数。这个想法是向工厂提供一个字符串,工厂又在形式和返回函数的主体中替换它。我已经设法使用 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>
可能有更简洁的方法,但这里有一个想法:
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()
)以将其从调用转换为函数。然后在第二行中,我们将第三个参数添加到正式参数列表中。最后一行 returns 函数。
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 = ...))
的更好替代方案,这绝对 是 不好的做法。
这是一个使用 pryr::make_function
的替代方法。这具有将函数 "environment" 设置为 "function factory" 调用
的调用环境的额外好处
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
中
这只使用基数 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)
我正在尝试创建一个函数工厂来创建具有自定义形式参数名称的函数。这个想法是向工厂提供一个字符串,工厂又在形式和返回函数的主体中替换它。我已经设法使用 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>
可能有更简洁的方法,但这里有一个想法:
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()
)以将其从调用转换为函数。然后在第二行中,我们将第三个参数添加到正式参数列表中。最后一行 returns 函数。
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 = ...))
的更好替代方案,这绝对 是 不好的做法。
这是一个使用 pryr::make_function
的替代方法。这具有将函数 "environment" 设置为 "function factory" 调用
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
这只使用基数 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)