在这个讲座中,Runar在前8分钟解释了Scala在尾调用消除方面存在问题,这让我想知道F#是否存在类似的问题?如果没有,为什么没有呢?
scala.util.control.TailCalls
object对象,它是一种重新定义的trampoline。 (参见Stackless Scala With Free Monads by Rúnar Óli Bjarnason,了解此想法的更普遍版本,该版本可以消除对堆栈的所有使用,而不仅仅是在尾调用中。) 这可用于在Scala中实现尾调用算法,但这与JVM堆栈不兼容,即它对其他语言或调试器不像递归方法调用:import scala.util.control.TailCalls._
def isEven(xs: List[Int]): TailRec[Boolean] =
if (xs.isEmpty) done(true) else tailcall(isOdd(xs.tail))
def isOdd(xs: List[Int]): TailRec[Boolean] =
if (xs.isEmpty) done(false) else tailcall(isEven(xs.tail))
isEven((1 to 100000).toList).result
def fib(n: Int): TailRec[Int] =
if (n < 2) done(n) else for {
x <- tailcall(fib(n - 1))
y <- tailcall(fib(n - 2))
} yield (x + y)
fib(40).result
Clojure有recur
特殊形式,也是一个显式的trampoline。
F#没有尾调用问题。它的处理方式如下:
如果你有一个单一的尾递归函数,编译器会生成一个循环并进行突变,因为这比生成.tail
指令更快。
在其他尾调用位置(例如使用continuations或两个相互递归的函数),编译器生成.tail
指令,所以尾调用由CLR处理。
默认情况下,在Visual Studio的Debug模式中,尾调用优化被关闭,因为这样可以更容易地进行调试(可以检查堆栈),但如果需要,可以在项目属性中打开它。
在旧版本中,.tail
指令在某些运行时(CLR x64和Mono)上存在问题,但现在已经修复了所有问题,一切正常。
tail
操作码支持尾调用优化(.NET 4进行了一些改进),就我所知,JVM目前还不支持尾调用。 - Patryk Ćwiekodd
/even
。 - Ionuț G. Stantail
操作码。 - Patryk Ćwiek