This function is a tail recursion. How to prove this ?
As given by other answers if it were not tail recursion then the annotation @tailrec would give the compilation error.
The other way to prove this function is a tail recursion is to use the getStackTrace() method of Thread class as given below.
package tailRecursion
object Fibonaaci {
def fib(n: Int): Int = {
var count = 2
def go(a: Int, b: Int): Int = {
count += 1
val c = a + b
if (n==0) {
0
}
else if (n==1) {
1
}
else if (count==n) {
val x = Thread.currentThread.getStackTrace
x.foreach{println _}
c
}
else go(b, c)
}
go(0, 1)
}
def main(args: Array[String]): Unit = {
val result = fib(10)
println(result)
}
}
The output of the above code is given below.
java.lang.Thread.getStackTrace(Thread.java:1552)
tailRecursion.Fibonaaci$.go$1(Fibonaaci.scala:17)
tailRecursion.Fibonaaci$.fib(Fibonaaci.scala:23)
tailRecursion.Fibonaaci$.main(Fibonaaci.scala:27)
tailRecursion.Fibonaaci.main(Fibonaaci.scala)
34
At the time we call the getStackTrace method (line 17) on the current thread we have only one stack frame used by go method. That is the reason we are only seeing one “go” in the above output.
Let us consider another example which is not a tail recursion. This time let us see the non tail recursion of factorial method.
package tailRecursion
object FactorialNoTailRec {
def factorial(n:Int) : Int = if (n == 0) {
val x = Thread.currentThread.getStackTrace
x.foreach{println _}
1
} else {
n * factorial(n-1)
}
def main(args: Array[String]): Unit = {
println("factorial without tail rec started")
val result = factorial(10)
println(result)
}
}
The output of the above command is given below.
factorial without tail rec started
java.lang.Thread.getStackTrace(Thread.java:1552)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:6)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.factorial(FactorialNoTailRec.scala:10)
tailRecursion.FactorialNoTailRec$.main(FactorialNoTailRec.scala:15)
tailRecursion.FactorialNoTailRec.main(FactorialNoTailRec.scala)
3628800
From the above output it clear that we have 11 stack frames created for factorial method because it is not tail recursion method.
Now let us see the tail recursion version of factorial method.
package tailRecursion
object FactorialTailRec {
def factorial(n:Int, sum:Int) : Int = if (n == 0) {
val x = Thread.currentThread.getStackTrace
x.foreach{println _}
sum
} else {
factorial(n-1, sum * n)
}
def main(args: Array[String]): Unit = {
println("factorial without tail rec started")
val result = factorial(10, 1)
println(result)
}
}
The output of the above tail recursion version of factorial is given below.
factorial without tail rec started
java.lang.Thread.getStackTrace(Thread.java:1552)
tailRecursion.FactorialTailRec$.factorial(FactorialTailRec.scala:6)
tailRecursion.FactorialTailRec$.main(FactorialTailRec.scala:15)
tailRecursion.FactorialTailRec.main(FactorialTailRec.scala)
3628800
The tail recursion version produced the same output which is 3628800. However it uses only 1 stack frame for the factorial method instead of 11.
I tried to find some sort of instrumentation techniques to analyze Java stack memory used by its threads to prove this.
But the best thing I found in our case is the getStackTrace() method to prove this method is tail recursion.