在另一个函数中将包和函数作为参数传递

7
我正在寻找在R中不同包中特定函数的方法。例如,methods(broom::tidy)将返回包broom中函数tidy的所有方法。对于我的当前问题,如果我可以像这样在另一个函数中使用methods函数,那就更好了: f1 <- function(x,y){ methods(x::y) } (我删掉了与我的问题无关的其他代码部分。) 但是当我按照以下方式运行该函数时:
f1 <- function(x,y){ methods(x::y)}
f1(broom,tidy)

我遇到了以下错误:

Error in loadNamespace(name) : there is no package called ‘x’

如果我尝试修改函数但保持包不变,我会得到类似的错误:

f2 <- function(y){  methods(broom::y)}
f2(tidy)

错误:从'namespace:broom'中未导出对象'y'

如何使函数中的包和函数名称正确地评估?当前的问题是否与r在函数中尝试评估/替换值时有关?

1个回答

7

::methods() 函数都使用非标准评估方式来工作。这意味着您需要在向函数传递值时更加聪明,以使其正常工作。以下是一种方法:

f1 <- function(x,y){ 
  do.call("methods", list(substitute(x::y)))
}
f1(broom,tidy)

在这里,我们使用 substitute() 函数将传递的 xy 值扩展到命名空间查找中。这解决了您可以通过以下方式查看的 :: 部分:

f2 <- function(x,y){ 
  substitute(x::y)
}
f2(broom,tidy)
# broom::tidy

我们需要使用替代物,因为可能会存在一个具有函数“y”的包“x”。因此,在使用“::”时,变量不会被展开。请注意,“::”只是一种使用字符值从命名空间中提取值的getExportedValue()的包装器,如果您需要提取值,则应这样做。

但是还有一个问题:methods()不评估其参数,它使用原始表达式来查找方法。这意味着我们实际上不需要broom :: tidy的值,而是需要传递该文字表达式。由于我们需要评估替代以获取所需的表达式,因此我们需要使用do.call()构建调用,以便评估substitute并将该表达式传递给methods()


我该如何将 f2(dplyr, between) 转换为函数?它返回未求值的函数调用。类似于 f2(dplyr, between)(1,2,3) 的东西。 - slava-kohut
1
@slava-kohut。嗯,你可以直接调用\::`(dplyr, between)(1, 2, 3),或者你可以使用eval()表达式eval(f2(dplyr, between))(1, 2, 3)`。 - MrFlick
这是我要求的。谢谢! - slava-kohut
@MrFlick 有点随机,但是是否有一个纯rlang的解决方案?我尝试用enexprenquo替换substitute,但是出现了参数必须是符号的错误。我最初认为substituterlang中与enexpr相同,但现在我意识到substitute可能具有更多功能。 - Mike
1
@Mike 这是我能让rlang正常工作的唯一方法:f3 <- function(x,y){ x <- rlang::ensym(x); y <- rlang::ensym(y);rlang::eval_tidy(rlang::quo(methods(::(!!x, !!y))))} 解析似乎在 :: 操作符周围有些古怪。 - MrFlick
@MrFlick 谢谢!我想我会坚持使用 substitute,但我很感激 rlang 的解决方案! - Mike

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接