理解Scala中Tail递归背后的想法

时间:2017-12-31 16:05:24

标签: scala recursion

我来自面向对象的背景,我主要用Java编写应用程序。我最近开始在Scala上探索更多,我一直在阅读一些文字。因此我遇到了一种称为尾递归的东西。我明白了如何编写尾递归方法。

例如 - 要在List中添加元素(当然这可以使用reduce方法完成)但是为了理解,我写了一个尾递归方法:

@scala.annotation.tailrec
def sum(l: List[Int], acc: Int): Int = l match {
  case Nil => acc
  case x :: xs => sum(xs, acc + x)
}

Scala运行时如何在内部处理此递归?

1 个答案:

答案 0 :(得分:3)

  

Scala运行时如何在内部处理此递归?

不是。它在编译时由编译器处理。

尾递归相当于while循环。因此,尾递归方法可以编译为while循环,或者更确切地说,它可以编译为while循环编译的相同方式。当然,完全的编译方式取决于所使用的编译器。

目前有三种主要的Scala实现,它们是Scala-native(一种针对具有自己的运行时的本机机器代码的编译器),Scala.js(一种针对ECMAScript平台的编译器,位于ECMAScript运行时的顶部) ),以及混乱的JVM实现Scala也被称为" Scala"就像语言一样(针对JVM平台并使用JVM运行时)。曾经有一个Scala.NET,但不再主动维护。

我将在这个答案中专注于Scala-JVM。

我将使用与您的示例略有不同的示例,因为模式匹配的编码实际上相当复杂。让我们从最简单的尾递归函数开始:

def foo(): Unit = foo()

这由Scala-JVM编译为以下JVM字节码:

public void foo()
  0: goto 0

还记得我上面说过尾递归相当于循环吗?好吧,JVM没有循环,它只有GOTO。这与while循环完全相同:

def bar(): Unit = while (true) {}

获取编译为:

public void bar()
  0: goto 0

还有一个更有趣的例子:

def baz(n: Int): Int = if (n <= 0) n else baz(n-1)

编译为:

public int baz(int);
   0: iload_1
   1: iconst_0
   2: if_icmpgt  9
   5: iload_1
   6: goto      16
   9: iload_1
  10: iconst_1
  11: isub
  12: istore_1
  13: goto       0
  16: ireturn

如您所见,它只是一个while循环。