Kotlin Tutoial
Basics
Control Flow
Array & String
Functions
Collections
OOPs Concept
Exception Handling
Null Safety
Regex & Ranges
Java Interoperability
Miscellaneous
Android
Recursion occurs when a function calls itself. While recursion can provide elegant solutions to certain problems, traditional recursion can lead to stack overflow errors if the depth of recursion is too large. Kotlin provides a way to optimize certain recursive functions to use a loop instead of a deep call stack, a technique known as "tail recursion."
In this tutorial, we'll explore tail recursion in Kotlin.
Tail recursion is a form of recursion where the recursive call is the last operation in the function. Because of this structure, the Kotlin compiler can optimize tail recursive functions to avoid stack overflow errors, turning them into loops under the hood.
tailrec
modifierIn Kotlin, you can indicate that a function is tail recursive using the tailrec
modifier. This tells the compiler to optimize the function if possible.
Let's implement the factorial function recursively, then optimize it with tail recursion.
fun factorial(n: Int): Int { if (n == 0) return 1 return n * factorial(n - 1) }
To make the above function tail recursive, we can use an accumulator:
tailrec fun factorialTailRec(n: Int, accumulator: Int = 1): Int { if (n == 0) return accumulator return factorialTailRec(n - 1, n * accumulator) }
Here, accumulator
holds the result so far, allowing the recursive call to be the last operation in the function.
The Fibonacci sequence is another common example where tail recursion can be beneficial.
fun fibonacci(n: Int): Int { if (n <= 1) return n return fibonacci(n - 1) + fibonacci(n - 2) }
To make this tail recursive, we'll maintain two accumulators:
tailrec fun fibonacciTailRec(n: Int, a: Int = 0, b: Int = 1): Int { if (n == 0) return a return fibonacciTailRec(n - 1, b, a + b) }
Not all recursive functions can be made tail recursive. Only those where the recursive call is the very last operation can be made tail recursive.
You must use the tailrec
modifier. Otherwise, the compiler won't optimize it.
You can verify tail recursion optimization by looking at the generated bytecode (and decompiling it to Java, if necessary). The recursive function will be transformed into a loop, which is an indication that tail recursion optimization has been applied.
Tail recursion is a powerful feature in Kotlin that allows developers to express algorithms recursively without the fear of stack overflow errors in deep recursions. By leveraging the tailrec
modifier, you can maintain the clarity of recursive solutions while gaining the performance benefits of iterative ones.
Optimizing recursion with the tailrec keyword in Kotlin:
tailrec
keyword on a function to enable the tail call optimization.tailrec fun factorial(n: Int, acc: Long = 1): Long = if (n == 0) acc else factorial(n - 1, acc * n)
Handling large computations with tail recursion in Kotlin:
tailrec fun power(base: Int, exponent: Int, result: Int = 1): Int = if (exponent == 0) result else power(base, exponent - 1, result * base)
Mutual tail recursion in Kotlin:
tailrec fun isEven(n: Int): Boolean = if (n == 0) true else isOdd(n - 1) tailrec fun isOdd(n: Int): Boolean = if (n == 0) false else isEven(n - 1)