在R语言中,如何在“::”符号调用中程序化地切换包?

8

假设有一个调用 bar::foo() 函数的语句,我希望能够以编程方式切换到包 hello,使得相同的语法可以调用 hello::foo()

示例:

  • Let's say I have three packages, parentPkg, childPkg1 and childPkg2.
  • In parentPkg I have a call to function childPkg1::foo()
  • foo() is also a function in childPkg2
  • I would like to be able, in parentPkg to use the :: operator to call foo() but to programatically switch the package name. Something like:

    dummy_pkg_name = ifelse(scenario=="child1", "childPkg1", "childPkg2")
    dummy_pkg_name::foo()
    

这可行吗?我该如何实现它?

一些背景信息

parentPkg 是一个与 Web 应用程序交互的函数,接受某些请求和数据,并根据场景从不同的统计模型返回结果。每个场景都非常复杂,parentPkg 无法将所有内容概括。因此,childPkg1childPkg2(实际上还有3和4)是一些子包,用于处理数据清理和每个场景的各种替代方案,但返回相同的值类别。
思路是:parentPkg 根据场景切换到相应的子包,并调用所有必要的函数,而无需为每个子包编写相同的序列,只需稍微不同的 :: 调用即可。


我认为,如果(requireNamespace('childPkg1'))是你真正想要的。 - MichaelChirico
3个回答

12

由于::可以被视为一个函数,因此它看起来像

`::`(dummy_pkg_name, foo)()

这是您想要的。或者,

getFromNamespace("foo", ns = dummy_pkg_name)()

例如,
`::`(stats, t.test)
# function (x, ...) 
# UseMethod("t.test")
# <bytecode: 0x102fd4b00>
# <environment: namespace:stats>

getFromNamespace("t.test", ns = "stats")
# function (x, ...) 
# UseMethod("t.test")
# <bytecode: 0x102fd4b00>
# <environment: namespace:stats>

2
你可以创建一个 call(),然后进行评估。
call("::", quote(bar), quote(foo()))
# bar::foo()

投入使用:

c <- call("::", quote(stats), quote(t.test))
eval(c)
# function (x, ...) 
# UseMethod("t.test")
# <bytecode: 0x4340988>
# <environment: namespace:stats>

将内容包装成一个函数,使用setdiff作为我们的默认函数:

f <- function(pkg, fn = setdiff) {
    pkg <- substitute(pkg)
    fn <- substitute(fn)
    eval(call("::", pkg, fn))
}

f(base)
# function (x, y) 
# {
#     x <- as.vector(x)
#     y <- as.vector(y)
#     unique(if (length(x) || length(y)) 
#         x[match(x, y, 0L) == 0L]
#     else x)
# }
# <bytecode: 0x30f1ea8>
# <environment: namespace:base>

f(dplyr)
# function (x, y, ...) 
# UseMethod("setdiff")
# <environment: namespace:dplyr>

2
遵循KISS,只需在全局环境中重新分配新的命名函数。确保省略(),因为您不需要运行该函数。
parent_foo <- parentPkg::foo
child1_foo <- childPkg1::foo
child2_foo <- childPkg2::foo
child3_foo <- childPkg3::foo

然后,根据需要有条件地应用它们:
if (scenario=="child1") {
  obj <- child1_foo(...)
} 
else if (scenario=="child2") {
  obj <- child2_foo(...)
} 
...

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