call_modify 的嵌套使用

Nested use of call_modify

我正在尝试创建对函数 f 的调用,其第一个参数是对另一个函数的调用(我选择 dbinom 作为示例)。对 dbinom 的调用(传递给 f)不包括所有参数的值,因为这些参数应在 f 内完成,并且完成的调用由 f 返回.这是我失败的最小尝试:

f <- function(a_call) {
    call_modify(a_call, x=1)
}

a_call <- call2(dbinom, size=1, prob=0.5)
y <- call2(f, a_call)

y 的输出是:

(function(a_call) {
    call_modify(a_call, x=1)
})((function (x, size, prob, log = FALSE) 
.Call(C_dbinom, x, size, prob, log))(size = 1, prob = 0.5))

这次通话将

如果我计算 y,它会出错,因为缺少 dinom 的第一个参数。

我相似但相关的构造:

> call2(call2(dbinom, x=1, size=1, prob=0.5))

((function (x, size, prob, log = FALSE) 
.Call(C_dbinom, x, size, prob, log))(x = 1, size = 1, prob = 0.5))()
(function (x, size, prob, log = FALSE) 

我觉得我在这里尝试的东西 'not even wrong',嵌套调用修改最好以另一种方式完成。

如果我理解正确你想做什么, 那么也许这个效果更好:

f <- function(a_call) {
  call_modify(call_standardise(call2(ensym(a_call)),
                               caller_env()),
              x=1)
}

可以使用或不使用的字符:

f(print)
# print(x = 1)
f("print")
# print(x = 1)
eval(f(print))
# 1

或更间接:

a_call <- expr(print)
eval(call2(f, a_call))
# print(x = 1)
eval(expr(f(!!a_call)))
# print(x = 1)

由于我们在这里做了一些非标准的评估, 事情变得有点棘手。 call_standardise需要能够找到您指定的函数, 并且很有可能会在调用f的环境中找到, 并且不一定在调用 call_standardise 的环境中, 在这种情况下,这将是 f 的执行环境。 这就是为什么在调用 call_standardise 时明确指定 caller_env() 的原因,即使这是后者的 env 的默认设置, 因为默认参数是在函数的执行环境中计算的, 而显式参数是在调用者的环境中计算的。

下面是这个问题的一个看似人为设计的例子:

f2 <- function(a_call) {
  call_modify(call_standardise(call2(ensym(a_call))),
              x=1)
}

e <- new.env()
e$foo <- function(x) { x + 1 }
with(e, f(foo))
# foo(x = 1)
with(e, f2(foo))
# Error in eval_bare(node_car(expr), env) : object 'foo' not found

但是,如果您要开发一个提供 f 的包, 这个例子不再是人为的: f 会存在于您包裹的环境中, 和其他包可以调用它以获取仅在其各自命名空间中可用的功能。

有关更多细节和描述, 检查 this reference, 也许尝试为我的示例绘制调用树。

call2 通过将评估的 ... 参数传递给可调用对象(第一个参数)来构造调用。例如,下面的命令输出到控制台 "y" 作为传递给 call2 的第二个参数被评估,

> A <- call2(print, x=print('y'))
[1] "y"

并构造对 print 的调用,它以 x="y" 作为参数(而不是 x=print("y")):

> A
(function (x, ...) 
UseMethod("print"))(x = "y")

为了绕过 a_call 在构造的调用中被评估然后传递(给 f),可以引用它,例如

f <- function(a_call) {
    call_modify(a_call, x=1)
}

a_call <- call2(dbinom, size=1, prob=0.5)
y <- call2(f, quote(a_call))

现在:

> y
(function(a_call) {
    call_modify(a_call, x=1)
})(a_call)

purrr::partial() 似乎更自然地处理了您尝试做的事情,它填充了函数的一个或多个参数:

f <- function( a_fun ) {purrr::partial( a_fun, x=1 )}

a_fun <- purrr::partial( dbinom, size=1, prob=0.5 )
y <- f(a_fun)

y(...) 现在实际上是 dbinom( x=1, size=1, prob=0.5, ... )

y()            # 0.5
y(log=TRUE)    # -0.6931472

partial() 的伟大之处在于它可以自然地与 %>% 管道链接:

z <- partial(dbinom, size=1) %>% partial(prob=0.5) %>% partial(x=1)
z(log=TRUE)    # -0.6931472