【发布时间】:2020-10-21 05:13:08
【问题描述】:
我从 Scala 来到 Idris。 Scala 有尾调用优化 (TCO),如果它不能使用 TCO 优化递归函数,我可以告诉编译器阻止。例如,请参阅theseposts。
这个 Scala 函数成功计算 String 长度
def allLengths(strs: List[String]): List[Int] = strs match {
case Nil => Nil
case x :: xs => x.length :: allLengths(xs)
}
但是如果我用annotation.tailrec注释它,编译器会出错
错误:无法优化 @tailrec 注释方法 zip:它包含不在尾部位置的递归调用
因为该函数不直接返回对allLengths 的调用。如果我用很长的List 运行它(没有注释),我会得到“错误:递归过多”,正如预期的那样。
但是,我可以将其重写为
@tailrec
def allLengths(strs: List[String], acc: List[Int] = Nil): List[Int] = strs match {
case Nil => acc.reverse
case x :: xs => allLengths(xs, x.length :: acc)
}
与注释一起编译得很好。使用@tailrec,Scala 通过将代码转换为没有递归错误风险的命令式循环来编译代码。我相信它作为命令式循环也可能更快。
在布雷迪的Idris book 中,他使用了这个例子
allLengths : List String -> List Nat
allLengths [] = []
allLengths (x :: xs) = length x :: allLengths xs
可以用total 注释编译,我似乎不会导致递归错误(尽管allLengths (replicate 5000 "hi") 有困难)。来自 Scala,我很惊讶他没有以尾递归的方式编写它。几个问题:
- 对于非尾递归的递归函数,Idris 中是否可能出现运行时递归错误?
- 是否在编译期间在 Idris 中优化了尾递归函数?非尾递归呢?
- 是否有类似 Scala 中的注释来确保 TCO?
@tailrec感觉和total很像,但前者不保证整体性,后者不保证尾递归
【问题讨论】:
标签: tail-recursion idris