递归与尾递归
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
本文探讨Scala中的递归与尾递归,强调尾递归如何避免堆栈溢出问题。通过实例1展示了如何用尾递归计算阶乘,解释了`@tailrec`注解的作用。此外,还介绍了柯里化的概念,如何将多参数函数转换为单参数函数链,并在实例2中结合尾递归和柯里化计算区间函数的和。
2329

被折叠的 条评论
为什么被折叠?



