我正在尝试编写一个程序,它将表达式作为输入,并返回一个函数,该表达式绑定为其主体。
caller <- function (expr, params) {
Function <- function (params, body, env = parent.frame()) {
# returns a function
}
Function(params, body = expr)
}
func <- caller (a + b, c('a', 'b'))
func(1, 2)
[1] 3
我可以通过使用类似
之类的东西轻松绑定参数params <- c('a', 'b')
f <- function() {}
formals(f) <- structure(
replicate(length(params), NULL),
names = params
)
我无法想出一种动态添加表达式作为正文的方法。我已经尝试过使用substitute(),并从pryr库中调整make_function,但是我无法完成任务。我最好的尝试是
body(f, parent.frame()) <- as.list( match.call() )[-1]$body
我也无法使用替代品。关于如何绑定主体以使最顶层的程序按预期工作的任何想法?
我在SO上看过similar questions,但解决方案似乎并不能解决这个问题。
答案 0 :(得分:4)
简单地说:
caller <- function(expr, params) {
f <- function() NULL
formals(f) <- structure(replicate(length(params), NULL), names=params)
body(f, envir=parent.frame()) <- substitute(expr)
f
}
它不使用内部函数,这可能导致substitute
的问题。
请注意,我不确定这是否按照您想要的方式设置返回函数的环境。这会将其设置为您调用caller
的环境。
答案 1 :(得分:3)
这是一个允许参数没有默认值的解决方案。传递参数名称也更容易,因为它们不必用引号括起来。
请检查以下代码中的评论:
g <- function(...)
{
# Get the arguments as unevaluated expressions:
L <- as.list(substitute(list(...)))[-1]
# The first argument is the body expression (technically a call object):
expr <- L[[1]]
# If the expression is not enclosed in curly braces, let's force it:
if( as.character(expr[[1]]) != "{" ) expr <- call("{", expr)
# Drop the first argument:
L <- L[-1]
# Mark symbols to be used as names for missing parameters:
filter <- vapply(L, is.symbol, logical(1))
params <- L
# The obscure expression "formals(function(x){})$x" returns a missing value, something really arcane ;-) :
params[filter] <- list(formals(function(x){})$x)
# Here the symbols are used as names:
names(params)[filter] <- vapply(L[filter], as.character, character(1))
# Now the result:
f <- function(){}
formals(f) <- params
body(f) <- expr
# Just to make it nicier, let's define the enclosing environment as if the function were created outside g:
environment(f) <- parent.frame()
f
}
一些测试:
> g(a+b, a, b=1)
function (a, b = 1)
{
a + b
}
> f <- g({x <- a+b; x^2}, a, b)
> f
function (a, b)
{
x <- a + b
x^2
}
> f(2,3)
[1] 25
> f(1)
Error in a + b : 'b' is missing
> g(a+b, a=2, b=2)()
[1] 4
答案 2 :(得分:0)
指定函数参数的一种有趣的替代方法是使用与alist
函数相同的机制,该函数通常与formals
一起使用。这就是它在base
包中的定义:
alist <- function (...) as.list(sys.call())[-1L]
这很容易适用于caller
:
caller <- function(...) {
f <- function() NULL
formals(f) <- as.list(sys.call())[-(1:2)]
body(f, envir=parent.frame()) <- substitute(list(...))[[2]]
f
}
第一个参数仍指定函数体,其余参数与alist
完全相同。
> func <- caller(a+b, a=, b=10)
> func(1)
[1] 11
> func <- caller(a+b, a=, b=a)
> func(10)
[1] 20
您甚至可以创建使用...
的函数:
> func <- caller(c(...), ...=)
> func("a", "b", "c")
[1] "a" "b" "c"