Recursion
Overstack
It's a very common problem when we use the recursion.
def factorial(num: Int): Int = {
if (num == 1) num
else num * factorial(num)
}
println(factorial(5))
Here is the recursion stack.
factorial(5)
5 * factorial(4)
5 * (4 * factorial(3))
5 * (4 * (3 * factorial(2)))
5 * (4 * (3 * (2 * factorial(1))))
5 * (4 * (3 * (2 * 1)))
5 * (4 * (3 * 2))
5 * (4 * 6)
5 * 24
120
To optimise that, we use the tail recursion.
Tail recursion
-
The tail recursion is the tail call itself.
-
The tail call is the function ONLY returns the call of another function.
def factorialOpt(num: Int, accumlator: BigInt): BigInt = {
if (num == 1) accumlator
else factorialOpt(num - 1, num * accumlator)
}
println(factorialOpt(5, 1))
Here is the recursion stack.
factorialOpt(5, 1)
factorialOpt(4, 5)
factorialOpt(3, 20)
factorialOpt(2, 60)
factorialOpt(1, 120)
120
See, tail recursion saves half of the memory and the time, which means reduce the both time and space complicity.
Actually, we cache the previous result in the parameter, accumlator
, so that we optimise the recursion.
To indicate a tail recurion in Scala, we use @tailrec
@tailrec
def factorialOpt(num: Int, accumlator: BigInt): BigInt = {
if (num == 1) accumlator
else factorialOpt(num - 1, * num accumlator)
}