library(foreach)
library(doMC)
myfun <- function(threshold){
val <- rnorm(1, mean = 0, sd = 1)
if(val > threshold){
stop("bad")
}else return(val)
}
results <- vector("list", length = 10)
parallel_fun <- function(reps, threshold){
registerDoMC(cores = 48)
results = foreach (j = 1:reps, .combine = rbind) %dopar% {
myfun(threshold)
}
}
> parallel_fun(reps = 10, threshold = 0)
Error in { : task 1 failed - "bad"
以上是一个简单的、可重复的例子。我想并行化myfun
以获得reps = 10
的复制次数。当生成的val
大于某个threshold
时,myfun
可能会停止。在这种情况下,我想停止运行myfun
,而不是让它返回val
。最后,我希望我的results
有10个vals
,大于一些threshold
。因此,我认为在这里使用while循环可能更合适,因为我想让它一直运行,直到有10个值满足threshold
。是否有可能重新使用foreach
来并行化while循环?
控制流程
通常不鼓励对控制流使用异常。理想情况下,
使用你想要的函数
在这个特定的示例中,您正在模拟截断正态分布。因此,您可以使用截断规范包中的truncnorm
函数。
重写函数或者,重写myfun
以始终返回正确的值:
myfun = function(threshold){
repeat{
val = rnorm(1, 0, 1)
if(val <= threshold)
break
}
val
}
这只是一种可能的变体。这里我使用自定义的do-while
结构。
请注意,根据阈值的不同,可能会发生大量或可能无限次的迭代,因此要小心行事,要么设置最大迭代次数,要么在threshold
没有超出所讨论的函数的最大范围时进行一些初步检查,最好是两者都进行。
有了这个,你应该能够像现在一样轻松地运行foreach
。
写一个包装器
如果您无法控制myfun
,则需要构造包装器,该构造可能与上面的函数几乎相同:
wrap_myfun = function(threshold){
repeat{
val = try(myfun(threshold))
if(is.numeric(val))
break
}
val
}
跟踪迭代:
如果您需要跟踪生成上述数字所需的迭代次数,您可以将repeat
重写为for
循环,或者只是添加计数器和另一个选项:
wrap_myfun = function(threshold, .maxiter=10^9, .default=NA){
iter = 1
repeat{
val = try(myfun(threshold))
if(is.numeric(val))
break
if(iter >= .maxiter){
val = .default
break
}
iter = iter + 1
}
list("value"=val, "iterations"=iter)
}
或者,代替分配默认值,您可以使用' stop('达到的最大迭代';)。那要看问题有多严重。
这样,您已经将所有逻辑移动到数据生成函数中,并且您不必管理在foreach
中实现的队列。负载应该在内核之间平均分配(超过一些迭代的可能随机的长计算时间,但这是您无法影响的)。