Scala类库中的有些注解,可以控制编译器优化,即将开始介绍
1、尾递归 @tailrec
object Module_WeiDG {
@tailrec
def story(): Unit = {
println("从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事")
story()
}
}
尖叫提示:进入下一个函数,不再需要上一个函数的环境了,得出结果以后直接返回。尾递归调用,可以被转化成循环,这样能节约栈空间
2、非尾递归
def story(): Unit = {
println("从前有座山,山上有座庙,庙里有个老和尚,一天老和尚对小和尚讲故事")
story()
println("小和尚听了,找了一块豆腐撞死了")
}
尖叫提示:下一个函数结束以后,此函数还有后续,所以必须保存本身的环境以供处理返回值。
3、尾递归优化案例
递归调用有时能被转化为循环,这样能节约栈空间。在函数式编程中,这是很重要的,我们通常会使用递归方法来遍历集合
(1)非尾递归
object Util {
def sum(xs: Seq[Int]): BigInt = {
if (xs.isEmpty) 0 else xs.head + sum(xs.tail)
}
...
}
上面的sum方法无法被优化,因为计算过程中最后一步是加法,而不是递归调用。调整后的代码
(2)尾递归优化
def sum2(xs: Seq[Int], partial: BigInt): BigInt = {
if (xs.isEmpty) partial else sum2(xs.tail, xs.head + partial)
}
Scala编译器会自动对sum2应用“尾递归”优化。如果你调用sum(1 to 1000000) 将会发生一个栈溢出错误。不过sum2(1 to 1000000, 0) 将会得到正确的结果。
尽管Scala编译器会尝试使用尾递归优化,但有时候某些不太明显的原因会造成它无法这样做,比如比较复杂的逻辑,如果你想编译器无法进行优化时报错,则应该给你的方法加上@tailrec注解,这样一来,如果编译器无法应用该优化,它就会报错
(3)蹦床
蹦床,感兴趣可以自己多了解了解
import scala.util.control.TailCalls._
def evenLength(xs: Seq[Int]): TailRec[Boolean] = {
if(xs.isEmpty) done(true) else tailcall(oddLength(xs.tail))
}
def oddLength(xs: Seq[Int]): TailRec[Boolean] = {
if(xs.isEmpty) done(false) else tailcall(evenLength(xs.tail))
}
// 获得TailRec对象获取最终结果,可以用result方法
evenLength(1 to 1000000).result
尖叫提示:对于消除递归,一个更加通用的机制叫做“蹦床”。蹦床的实现会将执行一个循环,不停的调用函数。每个函数都返回下一个将被调用的函数。尾递归在这里是一个特例,每个函数都返回它自己。
Scala有一个名为TailCalls的工具对象,帮助我们轻松实现蹦床。相互递归的函数返回类型为TailRec[A],其要么返回done(result),要么返回tailcall(fun)。其中,fun是下一个被调用的函数。这必须是一个不带额外参数且同样返回TailRec[A]的函数上面是一个简单的示例
转载于:https://www.jianshu.com/p/b1d09316ccd6