6

我正在尝试编写一个程序,该程序将表达式作为输入并返回一个以该表达式绑定为主体的函数。

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
)

我无法想出一种将表达式动态添加为主体的方法。我已经尝试使用替代(),并从 pryr 库中调整 make_function,但我不能完全让事情正常工作。我最好的尝试是

    body(f, parent.frame()) <- as.list( match.call() )[-1]$body

我也无法让它与替代品一起使用。关于如何绑定身体以使最顶层的程序按预期工作的任何想法?

我在 SO 上看到过类似的问题,但解决方案似乎不能满足这个问题。

4

3 回答 3

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

于 2013-05-11T14:26:30.257 回答
4

这是允许没有默认值的参数的解决方案。传递参数名称也更容易,因为它们不必用引号括起来。

请检查以下代码中的注释:

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
于 2013-05-13T16:52:48.777 回答
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"
于 2013-05-19T20:11:12.070 回答