【问题标题】:R package: using lapply to do.call a list of internal functionsR包:使用lapply来做。调用内部函数列表
【发布时间】:2018-10-18 08:22:18
【问题描述】:

我正在开发一个 R 包,其中我有一个导出函数,该函数需要调用几个未导出的函数并将每个函数的结果存储在一个列表中。 调用哪些函数是可变的,取决于用户输入。

我的处理方法是 lapply 一个函数名称的(字符)向量与 do.call,但这似乎使未导出的函数对导出的函数不可见。

考虑以下示例包代码:

tmp1 <- function(x) print(paste("Function 1 called with x =", x))
tmp2 <- function(x) print(paste("Function 2 called with x =", x))
tmp3 <- function(x) print(paste("Function 3 called with x =", x))

#' @export
test1 <- function() {
  tmp1("test")
  tmp2("test")
  tmp3("test")
}

#' @export
test2 <- function() {
  funs <- c("tmp1", "tmp2", "tmp3")
  for (fun in funs) do.call(fun, list(x = "test"))
}

#' @export
test3 <- function() {
  funs <- c("tmp1", "tmp2", "tmp3")
  lapply(funs, do.call, list(x = "test"))
}

构建和加载包后,运行三个test 函数会产生以下输出:

test1()
#> [1] "Function 1 called with x = test"
#> [1] "Function 2 called with x = test"
#> [1] "Function 3 called with x = test"

test2()
#> [1] "Function 1 called with x = test"
#> [1] "Function 2 called with x = test"
#> [1] "Function 3 called with x = test"

test3()
#> Error in tmp1(x = "test"): could not find function "tmp1"

直接调用函数有效,直接用do.call调用时用do.call调用有效,但用lapply调用时失败。 我可以使用 for 循环解决问题,但我很好奇为什么会发生这种情况。

所以,我的问题是双重的:

  1. 为什么在 lapply 内部调用时,未导出的函数对 do.call 不可见?
  2. 我可以让lapply(funs, do.call, list(...)) 方法起作用吗?

【问题讨论】:

  • 我无法重现您的错误。当我用你的代码创建一个包时,它对我来说很好。但是,无论如何,我认为当你编写一个包时,你应该使用::指定这些函数的位置。
  • 使用funs &lt;- c(tmp1, tmp2, tmp3) 代替funs &lt;- c("tmp1", "tmp2", "tmp3") 能解决您的问题吗?或者做lapply(mget(funs), do.call, list(x = "test")),这相当于
  • @Moody_Mudskipper 它使test3 运行,但它没有回答我的问题(即为什么 lapply 方法失败),此外,在我的实际包中, funs 将是用户(在某种程度上)指定的字符向量。
  • 然后使用mget 应该可以解决它,尽管它没有回答为什么它不起作用。恕我直言,使用函数对象更干净。
  • do.call 默认情况下在parent.frame() 中评估其字符参数,当使用lapply 时,您正在做一些使您的代码失败的环境体操(不幸的是,我不知道多说更多),因此您也可以通过调整 envir 参数来解决您的问题。来自?do.callenvir an environment within which to evaluate the call. This will be most useful if what is a character string and the arguments are symbols or quoted expressions.

标签: r package


【解决方案1】:

这部分已在其他地方介绍过,我建议您在 stackoverflow 上阅读这两个(12)线程,了解全局变量和范围分配。

为解决您的问题,请在分配功能时添加“&lt;&lt;-”,即

tmp1 <<- function(x) print(paste("Function 1 called with x =", x))
tmp2 <<- function(x) print(paste("Function 2 called with x =", x))
tmp3 <<- function(x) print(paste("Function 3 called with x =", x))

输出:

> test1()
[1] "Function 1 called with x = test"
[1] "Function 2 called with x = test"
[1] "Function 3 called with x = test"
> 
> test2()
[1] "Function 1 called with x = test"
[1] "Function 2 called with x = test"
[1] "Function 3 called with x = test"
> test3()
[1] "Function 1 called with x = test"
[1] "Function 2 called with x = test"
[1] "Function 3 called with x = test"
[[1]]
[1] "Function 1 called with x = test"

[[2]]
[1] "Function 2 called with x = test"

[[3]]
[1] "Function 3 called with x = test"

【讨论】:

  • 这样做会导致devtools::documenttmp 函数的定义放在全局环境中,这绝对不是我想要的,并且从新会话中加载包,test3 仍然没有'不起作用(事实上,test 函数都不起作用)
【解决方案2】:

根据问题上的 cmets,我有一个(部分)答案:

  1. do.call 默认情况下评估 parent.frame() 中的字符参数,当在 lapply 内部调用时,这是与定义 tmp 函数的环境不同的环境(尽管我不确定具体细节)李>
  2. do.call 提供当前环境使lapply 方法起作用: lapply(funs, do.call, args = list(x = "test"), envir = environment())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 2015-10-12
    • 2016-04-07
    • 2011-12-22
    • 1970-01-01
    • 2017-05-05
    相关资源
    最近更新 更多