在foreach循环中找不到函数

21

我正在尝试使用foreach在R中进行多核计算。

A <-function(....) {
    foreach(i=1:10) %dopar% {
    B()
    }
}

然后我在控制台中调用函数A。问题是,在B中调用了另一个脚本文件中定义的Posdef函数。我不得不将Posdef包含在foreach的导出参数列表中:.export=c("Posdef")。但是我遇到了以下错误:

Error in { : task 3 failed - "could not find function "Posdef""

为什么 R 找不到这个定义的函数?


1
我建议搜索关于R中动态作用域的信息。这并不直观,你会发现很多在SO上的问题详细描述了人们在函数内定义函数时遇到的问题。 - Brandon Bertelsen
我们希望提供一个可重现的示例。另外,您尝试过用snowfall进行多核处理吗?我觉得它更直观、更易于适应(修改apply函数)。 - Roman Luštrik
同样的问题,目前还没有找到简单的答案。我通过调用4个核心来使它工作,foreach(j=1:N, .combine=rbind, .packages=c(需要调用所有函数的包的向量)) %dopar% fun(j)。它提高了速度,但并没有除以4(只除以2)。我不确定自己在做什么,所以不要复制这个方法。 - RockScience
我通过在这里建议的clusterExport(cl,'myMean')中导出每个有问题的函数来解决了我的问题。https://dev59.com/QXTYa4cB1Zd3GeqP0e90?noredirect=1&lq=1 - user3226167
3个回答

16

为了让大家了解,我可以重现这个问题:

require(doSNOW)
registerDoSNOW(makeCluster(5, type="SOCK"))
getDoParWorkers()
getDoParName()
getDoParVersion()

fib <- function(n) {
  if (n <= 1) { return(1) }
  return(fib(n-1) + fib(n-2))
}

my.matrix <- matrix(runif(2500, 10, 50), nrow=50)

calcLotsaFibs <- function() {
  result <- foreach(row.num=1:nrow(my.matrix), .export=c("fib", "my.matrix")) %dopar% {
    return(Vectorize(fib)(my.matrix[row.num,]))
  }
  return(result)
}

lotsa.fibs <- calcLotsaFibs()

我已经通过将该函数放在另一个文件中并在foreach的body中加载该文件来解决这个问题。显然,您也可以将函数定义移动到foreach本身的body中。

[编辑--我之前曾建议可能.export与函数名称不能正常工作,但在下面得到了纠正。]


2
".export" 可以接受函数名称。这是一个有趣的例子,然而,我认为问题在于当 fib 对自身进行递归调用时出现了错误,因为 fib 不在其自己的作用域内,正如我在我的答案中所解释的那样。 - Steve Weston
你的意思是如果我们将自定义函数打包成R包,它就可以工作? - agenis

11
简短的回答是,这是并行后端(如doSNOWdoParalleldoMPI)中的一个bug,但已经得到修复。
略微详细一点的回答是,foreach使用特殊的“导出”环境将函数导出给工作进程,而不是全局环境。这曾经对在全局环境中创建的函数造成问题,因为“导出”环境不在它们的作用域内,即使它们现在在同一个“导出”环境中定义。因此,它们无法看到在“导出”环境中定义的任何其他函数或变量,例如你的情况中的“Posdef”。 doSNOWdoParalleldoMPI后端现在通过“.export”将导出的函数关联到“导出”环境,而非全局环境,似乎已经解决了这些问题。

5
我以为这个问题已经解决了,但我仍然在使用R v 3.5.0RStudio v 1.1.453同时运行foreach v 1.4.4doMPI v 0.2.2doParallel v 1.0.14doSNOW v 1.0.16时遇到了同样的问题。 - Nemesi

1

如果使用foreach %dopar%出现问题,快速解决的方法是重新安装以下包:

install.packages("doSNOW")

install.packages("doParallel") 

install.packages("doMPI")

它在我的情况下起作用。


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