When programming with Java / kotlin, it is recommended to use tail recursive or iterative version? Is there any difference in performance?
I try to learn good habits in programming, and I stick to this problem I know that in Java, recursive functions can be "painful ass" (sometimes), and I try to implement as many tail versions of the function as possible Is it worth worrying about, or should it be done in an old-fashioned way?
tailrec fun tail_fibonacci(n : BigInteger,fib1 : BigInteger = BigInteger.ZERO,fib2 : BigInteger = BigInteger.ONE) : BigInteger { return when(n){ BigInteger.ZERO -> fib1 else -> tail_fibonacci(n.minus(BigInteger.ONE),fib1.plus(fib2),fib1) } } fun iterative_fibonacci(n: BigInteger) : BigInteger { var count : BigInteger = BigInteger.ONE var a : BigInteger = BigInteger.ZERO var b : BigInteger = BigInteger.ONE var c : BigInteger while(count < n){ count += BigInteger.ONE c = a + b a = b b = c } return b }
Solution
The biggest difference I see is the signature: Although iterative_ Fibonacci accepts a parameter and is very clear, but tail_ Fibonacci requires three, although default values are provided, which may surprise users However, you can create a wrapper function for it and even make the tailrec function local one
Except n.minus (BigInteger. One) vs count = BigInteger In addition to one, the result bytecode of compiling two functions should not be much different. You can check it yourself with a kotlin bytecode viewer
In terms of performance, there should be no predictable difference between the two implementations (also note that the JVM uses the JIT compiler to optimize the code at runtime), but of course, the tailrec function is more effective than ordinary recursive functions
As for code style (based on high opinion), many tail recursive solutions look more natural (and closer to mathematical symbols), while others don't (for example, when many parameters end up completely confused)
Therefore, I think tailrec should be used as a performance tool when you find a recursive definition more suitable for expressing code (especially as a method to avoid stack overflow, which requires all recursive calls to be tail calls)