【发布时间】:2012-01-04 13:06:30
【问题描述】:
我一直在研究递归和 TCO。似乎 TCO 可以使代码变得冗长并影响性能。例如我已经实现了接收 7 位电话号码并返回所有可能的单词排列的代码,例如464-7328 可以是“GMGPDAS ... IMGREAT ... IOIRFCU” 这是代码。
/*Generate the alphabet table*/
val alphabet = (for (ch <- 'a' to 'z') yield ch.toString).toList
/*Given the number, return the possible alphabet List of String(Instead of Char for convenience)*/
def getChars(num : Int) : List[String] = {
if (num > 1) return List[String](alphabet((num - 2) * 3), alphabet((num - 2) * 3 + 1), alphabet((num - 2) * 3 + 2))
List[String](num.toString)
}
/*Recursion without TCO*/
def getTelWords(input : List[Int]) : List[String] = {
if (input.length == 1) return getChars(input.head)
getChars(input.head).foldLeft(List[String]()) {
(l, ch) => getTelWords(input.tail).foldLeft(List[String]()) { (ll, x) => ch + x :: ll } ++ l
}
}
它很短,我不必花太多时间在这上面。但是,当我尝试在尾调用递归中执行此操作以获取 TCO 时。我必须花费大量时间并且代码变得非常冗长。我不会摆出整个代码来节省空间。 Here is a link to git repo link。可以肯定的是,你们中的很多人都可以写出比我更好、更简洁的尾递归代码。我仍然认为总体而言 TCO 更冗长(例如,阶乘和斐波那契尾调用递归具有额外的参数累加器。)但是,需要 TCO 来防止堆栈溢出。我想知道您将如何处理 TCO 和递归。 this thread 中具有 TCO 的 Akermann 的 Scheme 实现是我的问题陈述的缩影。
【问题讨论】:
-
总拥有成本与递归有什么关系? ;)
-
链接线程中没有 Haskell。您似乎正在讨论的代码在方案中。
-
事实上,在 Haskell 中,通常但并非总是如此,尾调用并不那么重要。例如,您上面的问题实际上是关于生成结果流(即惰性列表)。它可以非常简单地用 Haskell 中的标准列表工具编写。
-
为什么要强制使用尾调用优化?并非所有递归例程都应该是尾调用递归的。如果每个递归调用都有自己的状态,那么它要么不是尾调用,要么您必须维护自己的状态并传递它(这就是您的实现所做的)。在任何一种情况下,空间消耗行为都是相同的。如果你真的需要担心堆栈溢出,就使用迭代版本,没有任何法律禁止它。
-
@Kim Stebel,递归与维护者(所有者)的总拥有成本有关,你知道的。你不想让它在你手中炸毁(堆栈溢出);)
标签: scala recursion functional-programming scheme tail-call-optimization