@runzhliu
2018-01-28T07:31:06.000000Z
字数 2541
阅读 1138
Scala 递归
参考资料
1. https://codeday.me/bug/20171011/84027.html
2. http://blog.csdn.net/jasonding1354/article/details/50525735
3. 《快学 Scala》
4. 《深入理解 Scala》
5. 《图解算法》
6. https://twitter.github.io/scala_school/zh_cn/collections.html#fold
在函数式编程中,递归是随处可见的,递归比在命令式编程中更为重要。递归是表达函数的最常用方式,但是也有两个缺点:
栈在这里是一个重要的编程概念,要理解递归,必须理解什么是栈。计算机内部使用被称为调用栈的栈。使用栈虽然很方便,但是也要付出代价,存储详尽信息可能要占用大量的内存,每个函数调用都占用一定内存的话,如果栈很高,意味着计算机存储了大量函数调用的信息,一般情况下,会有两种选择:
嵌套方法是指一个很长的方法里还有其他「辅助」方法,阶乘是比较常见的需要辅助方法的情况。
import scala.annotation.tailrecdef factorial(i: Int): Long = {@tailrecdef fact(i: Int, accumulator: Int): Long = {if (i <= 1) accumulatorelse fact(i - 1, i * accumulator)}fact(i, 1)}factorial: (i: Int)Long(0 to 5) foreach(i => println(factorial(i)))
尾递归表示调用递归函数是该函数中最后一个表达式,该表达式的返回值就是所调用的递归函数的返回值。尾递归非常重要,因为这是把函数优化为循环的最重要的一种递归。循环可以消除潜在栈溢出的风险,同时也因为消除了函数调用开销而提升效率。原生的 JVM 还不支持尾递归优化,但 scalac 可以支持。
Scala 编译器对尾递归做了有限的优化。为了排除在生产环境中出现栈空间的崩溃,可以加一个 tailrec 关键词,编译器会告诉你代码是否正确地实现了尾递归的。如果 fact 不是尾递归,编译器就会抛出错误。
递归函数的核心是设计好递归表达式,并且确定算法的边界条件。要写出尾部递归并不容易,函数设计需要下苦心。
// 阶乘递归def factorial(n: BigInt): BigInt = {if (n <= 1) 1 else n * factorial(n - 1)}// 阶乘尾递归import scala.annotation.tailrecdef factorialTailRecursive(n: BigInt): BigInt = {@tailrecdef _loop(acc: BigInt, n: BigInt): BigInt =if (n <= 1) acc else _loop(acc * n, n - 1)_loop(1, n)}// 菲波那切数列递归def fibonacci(n: Int): Int =if (n <= 2) 1 else fibonacci(n - 1) + fibonacci(n - 2)// 菲波那切数列尾递归def fibonacciTailRecursive(n: Int): Int = {@tailrecdef _loop(n: Int, acc1: Int, acc2: Int): Int =if (n <= 2) acc2 else _loop(n - 1, acc2, acc1 + acc2)_loop(n, 1, 1)}// 求列表长度尾递归def lengthTailRecursive[A](ls: List[A]): Int = {@tailrecdef lengthR(result: Int, curList: List[A]): Int = curList match {case Nil => resultcase _ :: tail => lengthR(result + 1, tail)}lengthR(0, ls)}
在较旧的版本的 Scala 集合中有几个方法操作集合,foldLeft 和 reduceLeft 有一个比 foldRight 和 reduceRight 重大的优势,它们是尾递归的,可以在 Scala 的尾递归优化中获益。还记得尾递归必须是一次队规中最后一个运行的操作吗?
在 Scala 2.12.2 中,这个性能优势被取消了,因为都是用到了尾递归。(未考证,待查)
@tailrecprivate def foldl[B](start: Int, end: Int, z: B, op: (B, A) => B): B =if (start == end) zelse foldl(start + 1, end, op(z, this(start)), op)@tailrecprivate def foldr[B](start: Int, end: Int, z: B, op: (A, B) => B): B =if (start == end) zelse foldr(start, end - 1, op(this(end - 1), z), op)override /*TraversableLike*/def foldLeft[B](z: B)(op: (B, A) => B): B =foldl(0, length, z, op)override /*IterableLike*/def foldRight[B](z: B)(op: (A, B) => B): B =foldr(0, length, z, op)override /*TraversableLike*/def reduceLeft[B >: A](op: (B, A) => B): B =if (length > 0) foldl(1, length, this(0), op) else super.reduceLeft(op)override /*IterableLike*/def reduceRight[B >: A](op: (A, B) => B): B =if (length > 0) foldr(0, length - 1, this(length - 1), op) else super.reduceRight(op)
