这是一项练习" Scala中的功能编程,"第13章,实现一个用于解释尾递归函数的蹦床。
runTrampoline2
不是尾递归的,并且使用我的测试输入溢出堆栈。此外,tailrec
注释为runTrampoline2
提供了编译器错误。 runTrampoline
是尾递归的,并传递注释的编译时检查。
我最好的选择是,这两个蹦床之间的区别在于val
捕获或不捕获单位的方式,就像这里:
scala> val foo = println("abc")
val foo = println("abc")
abc
foo: Unit = ()
scala> foo
foo
scala> val bar: Int = {println("xyz"); 5}
val bar: Int = {println("xyz"); 5}
xyz
bar: Int = 5
scala> bar
bar
res8: Int = 5
否则这两个蹦床看起来和我一模一样。如果有人评论他们对这个问题很重要,我会包含Free monad和Suspend,Return和FlatMap构造函数的代码,但我不认为它们是。由runTrampoline2
s泄漏的副作用导致val
的尾部递归被破坏了吗?谢谢!
@annotation.tailrec
def runTrampoline[A](tra: Free[Function0,A]): A =
tra match {
// case Return(A)
case Return(a1) => a1
// case Suspend(Function0[A])
case Suspend(function0A1) => function0A1()
// case FlatMap(Free[Function0[_],A], A=>Free[Function0,B]]
case FlatMap(free1, aFree2) => free1 match {
// case Return(A)
case Return(a2) => runTrampoline(aFree2(a2))
// case Suspend(Function0[A])
case Suspend(function0A) => runTrampoline(aFree2(function0A()))
// case FlatMap(Free[Function0[_],A], A=>Free[Function0,B]]
case FlatMap(a0,g) =>
runTrampoline {
a0 flatMap { a0 => g(a0) flatMap aFree2 }
}
}
}
//@annotation.tailrec
def runTrampoline2[A](tra: Free[Function0,A]): A =
tra match {
// case Return(A)
case Return(a1) => a1
// case Suspend(Function0[A])
case Suspend(function0A1) => {
val a1: A = function0A1()
a1
}
// case FlatMap(Free[Function0[_],A], A=>Free[Function0,A]]
case FlatMap(free1, aFree2) => free1 match {
// case Return(A)
case Return(a2) => {
val free2: Free[Function0,A] = aFree2(a2)
val a3: A = runTrampoline2(free2)
a3
}
// case Suspend(Function0[A])
case Suspend(function0A) => {
val a2: A = function0A()
val free2: Free[Function0,A] = aFree2(a2)
val a3: A = runTrampoline2(free2)
a3
}
// case FlatMap(Free[Function0[_],A], A=>Free[Function0,B]]
case FlatMap(a0,g) =>
runTrampoline2 {
a0 flatMap { a0 => g(a0) flatMap aFree2 }
}
}
}
一个月前我问了一个类似的问题,关于打破尾递归的类型注释:Scala: type annotations make tail recursion check fail
Aivean解决了。这是蹦床的修正版本。每个递归调用都在包含它的案例的最后。
@annotation.tailrec
def runTrampoline3[A](tra: Free[Function0,A]): A =
tra match {
case Return(a1) => a1
case Suspend(function0A1) => {
val a1 = function0A1()
a1
}
case FlatMap(free1, aFree2) => free1 match {
case Return(a2) => {
val free2 = aFree2(a2)
runTrampoline3(free2)
}
case Suspend(function0A) => {
val a2 = function0A()
val free2 = aFree2(a2)
runTrampoline3(free2)
}
case FlatMap(a0,g) =>
runTrampoline3 {
a0 flatMap { a0 => g(a0) flatMap aFree2 }
}
}
}
答案 0 :(得分:4)
看起来Scala编译器只有在调用自身实际函数中的最后一个操作时才识别尾递归。
我反编译两个不同的例子来检查这个。
阶:
def rec:Int = rec
的java:
public final class _$$anon$1 {
private int rec() {
while (true) {}
}
}
阶:
def rec:Int = {
val i = rec
i
}
的java:
public final class _$$anon$1 {
private int rec() {
final int i = this.rec();
return i;
}
}