防止递归R函数中的无限递归/堆栈溢出错误

时间:2015-05-17 23:44:42

标签: r

创建一个简单的递归函数,如下所示。

var result = await collection.Find(query)
    .Project<Hamster>(Builders<Hamster>.Projection.Exclude(hamster => hamster.FirstName).
        Exclude(hamster => hamster.LastName))
    .ToListAsync();

当n设置为1e4时,它显示错误power <- function(x, n) { if(n >= 1) x * power(x, n-1) else 1 } 。正如错误消息所示,我更新了option参数,在这种情况下,遇到infinite recursion错误。

stack overflow

Scala支持尾递归,因此可以处理## error to run power 10000 times with defalult option options()$expressions #[1] 5000 power(2, 1e3) #[1] 1.071509e+301 power(2, 1e4) #Error: evaluation nested too deeply: infinite recursion / options(expressions=)? #Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)? ## expressions parameter updated and results in stack overflow options(expressions = 100000) power(2, 1e4) #Error: protect(): protection stack overflow 错误,并且我稍微更改了函数。

stack overflow

然而,似乎情况会变得更糟,因为当n设置为1e3时,更新的函数会抛出## tail recursion supported in Scala power.rec <- function(x, n, t = 1) { if(n < 1) t else power.rec(x, n-1, x*t) } 错误。当选项参数增加时处理,但当n变为1e4时遇到infinite recursion错误。

stack overflow

现在我的问题是

如何在没有错误的情况下运行此类功能?

提前致谢。

1 个答案:

答案 0 :(得分:5)

尾部呼叫优化未在R中实现,因为R提供对完整呼叫堆栈的访问。来自 Luke Tierney

  

关于问题:尾部调用优化不能在R中应用,至少不能以简单的方式应用,因为R的语义通过sys.xyz函数和parent.frame等提供对调用栈的访问。有可能进行一些语义更改,例如只保证对直接调用者的访问,但除非/直到函数调用机制的性能得到改进,否则没有多少意义。

评论有些陈旧(2011),我希望compiler包可以解决这类问题,但我无法让它发挥作用。所以你要把你的功能变成一个循环:

power.rec2 <- function(x, n, t = 1) {
  while(n > 0) {
    t <- x * t
    n <- n - 1
  }
  t
}
identical(power.rec(2, 10), power.rec2(2, 10))
# [1] TRUE
identical(power.rec(2, 1e2), power.rec2(2, 1e2))
# [1] TRUE
power.rec2(2, 1e3)
# [1] 1.071509e+301
power.rec2(2, 1e6)
# [1] Inf

注意尾递归优化正是这样做:将递归转换为循环。不幸的是,您只能手动执行此操作。