递归与尾递归
Scala中的循环都是通过递归(Recursive)实现的。递归的缺点是运算层层压在堆栈中,递归的层数过多时,很容易造成堆栈溢出的问题。Scala中利用尾递归(Tail Recursive Function)解决递归过程中可能导致的堆栈溢出的问题。
尾递归
所有递归形式的调用都出现在函数的末尾。当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中创建一个新的。
实例1:计算n!
递归方法实现n!的代码块如下:
def factorial(n: Int): Int =
if(n <= 0) 1
else n * factorial(n - 1)
尾递归方法实现n!的代码块如下:
@annotation.tailrec
def factorial(n: Int, m: Int): Int =
if( n<= 0) m
else factorial(n - 1, m * n)
注释
@annotation.tailrec
的作用是告诉编译器启动尾递归优化。参数n
表示阶乘的数字,参数m
表示累乘器,保存前面的递归调用的结果。m
拿到的一直是过去几次累乘的结果,因此只需要一个栈保留m
最新的结果,而不是需要多个栈。
def main(args: Array[String])= {
println(factorial(4, 1))
}
输出结果为:24
柯里化
柯里化函数
:把具有多个参数的函数转换为一条函数链。每个节点上是单一函数。例如:
def add(x: Int, y: Int)= x + y
等价于
def add(x: Int)(y: Int)= x + y // 柯里化语法
实例2:计算 ∑baf(x) ∑ a b f ( x )
其中f(x)
表示取区间[a, b]内的整数进行运算的函数。
def sum(f: Int => Int)(a: Int)(b: Int): Int = {
@annotation.tailrec
def loop(n: Int)(acc: Int): Int = {
if(n > b) acc
else loop(n + 1)(acc + f(n))
}
loop(a)(0)
}
该例结合使用了柯里化和尾递归。参数(f:Int => Int)
表示f(x)
。loop
函数完成递归累加的功能,参数(n: Int)
表示每次叠加的值,参数(acc: Int)
表示累加器,保存最近一次累加的结果。
println(sum(x => x)(1)(4))
println(sum(x => x * x)(1)(4))
计算结果:
10
30
另外一种表示方法为:
val square = sum(x => x * x)_
println(square(1)(4))
其中_
表示通配参数列表,sum
函数可变的参数为a
,b
。所以_
表示通配参数a
,b
。
计算结果同样为:
30