了解如何将递归转换为蹦床

时间:2014-04-01 15:37:38

标签: scala recursion tail-recursion trampolines

我最近读过关于蹦床作为消除尾部呼叫的方法。我想将我的一个功能转换为利用蹦床的东西,但我很难开始(我从OO世界来到这里)。

def buildTree (X:DenseMatrix[Double], Y:DenseVector[Double], minBucket:Int):Node = {
        // Get the split variable, split point and data for this data
        val (splitVar, splitPoint, leftX, leftY, rightX, rightY) = chooseSplit(X, Y, minBucket);
        // If we couldn't find a split, then we have a leaf
        if(splitVar == Double.NegativeInfinity){
            new Node(Y)
        }else{
            // Otherwise recursively build the children and create yourself as a vertex
            val left =  buildTree(leftX, leftY, minBucket))
            val right = buildTree(rightX, rightY, minBucket))
            new Node(Y, splitVar, splitPoint, left, right)
        }
    }

具体来说,如果我想要在“更多”()'声明,是吗?

1 个答案:

答案 0 :(得分:2)

您的buildTree方法不会进行任何尾调用,因此无法利用蹦床。尾调用是当方法的返回值是另一个方法调用的结果时的优化。它允许堆栈帧替换为被调用函数的堆栈帧。如果没有尾调用优化,递归函数调用将导致堆栈大小增加,从而可能导致堆栈溢出。您的buildTree方法会自行调用两次,但原始堆栈帧必须保留,以便在创建新Node时可以合并两个函数调用的结果。

JVM没有内置支持尾部优化,但Scala有一个函数调用自身的尾调用。 Scala通过跳转到函数的开头来替换这些递归函数调用,从而有效地将递归转换为迭代。不幸的是,这对于共同递归调用不起作用(例如,当方法A调用调用A的B时)。此处可以使用蹦床,或者基于特殊的monad,或者滥用异常处理。没有理由在其他地方使用蹦床。