尾递归(@tailrec)递归函数vs非递归函数scala堆栈溢出错误?

时间:2017-02-01 10:12:59

标签: scala recursion stack-overflow tail-recursion akka-stream

我的用例更复杂,但基本上下面是我想要实现的简化示例(我的原始代码是针对akka-stream):

offset := 0

//pump data
def pump(): Unit = {
    elem := poller.getNumberFromOffset(offset)

    elem match {
        case null => doSomething()
        case Completed => doSomethingElse()
        case _ => 
            offset += 1
            //check if the element is matched with a pre-supplied selector/filter function
            if(filterFunc(elem)) {
                doSomething2()
            } else {
                //if the element doesn't match; increase offset and try again; can sleep for a while here               
                pump()
            }
    }
}

问题是pump()函数可能导致堆栈溢出(因为泵函数在特定条件下被反复调用)。

我可以将函数写入非递归版本,如下所示:

offset := 0

//pump data
def pump(): Unit = {
    elem := poller.getNumberFromOffset(offset)
    while(elem != null && elem != Completed && !filterFunc(elem)) {
        offset += 1
        elem = poller.getNumberFromOffset(offset)
    }       

    elem match {
        case null => doSomething()
        case Completed => doSomethingElse()
        case _ =>           
            offset += 1
            doSomething2()
    }
}

但是我的用例要复杂得多;所以我真的很想使用递归函数,而不是将现有代码转换为while / for循环。

我的问题是“我应该这样做”,如果我只是简单地在第一个例子中加上@tailrec注释,那么scala编译器就会将泵视为尾递归函数(@tailrec def pump():Unit = {})。在我的情况下,pump()函数应该在固定间隔后分开调用;所以堆栈框架在这里真的没有必要。

感谢。

1 个答案:

答案 0 :(得分:1)

如果你放@tailrec并且编译器允许你运行它,这意味着你的代码片段有资格进行尾递归优化。内部scala编译器会将尾递归代码转换为循环。我不认为尾递归函数会导致stackoverflow错误。所以你的pump()函数如果满足@tailrec则不会导致stackoverflow。