
- Scala - Home
- Scala - Overview
- Scala - Features
- Scala - Environment Setup
- Scala - Build Tool (SBT)
- Scala - REPL
- Scala - Dot & Dotty
- Scala - Basic Syntax
- Scala - Hello World Program
- Scala - Identifiers
- Scala - Keywords
- Scala - Comments
- Scala - Code Blocks
- Scala - Semicolon
- Scala - Constructs
- Scala - Expressions
- Scala - Input and Output
- Scala - Optional Braces
- Scala - Underscore (_)
- Data Types and Variables
- Scala - Data Types
- Scala - Type Bounds
- Scala - Context Bound
- Scala - Variances
- Scala - Type Hierarchy
- Scala - Variables
- Scala - Variable Scopes
- Scala - Literals
- Scala - Numeric Types
- Scala - Boolean Types
- Scala - Char Type
- Scala - Unit Types
- Scala - Strings
- Scala - Arrays
- Scala - Null Type
- Scala - Nothing
- Scala - Any Type
- Scala - AnyRef Type
- Scala - Unified Types
- Scala - Dates and Times
- Scala - Ranges
- Scala - Multidimensional Arrays
- Scala - WrappedArray
- Scala - StringBuilder
- Scala - String Interpolation
- Scala - StringContext
- Scala - Type Casting
- Scala var vs val
- Scala Operators
- Scala - Operators
- Scala - Rules for Operators
- Scala - Arithmetic Operators
- Scala - Relational Operators
- Scala - Logical Operators
- Scala - Bitwise Operators
- Scala - Assignment Operators
- Scala - Operators Precedence
- Scala - Symbolic Operators
- Scala - Range Operator
- Scala - String Concatenation Operator
- Scala Conditional Statements
- Scala - IF ELSE
- Scala - IF-ELSE-IF-ELSE Statement
- Scala - Nested IF-ELSE Statement
- Scala Loop Statements
- Scala - Loop Statements
- Scala - while Loop
- Scala - do-while Loop
- Scala - Nested Loops
- Scala - for Loop
- Scala - break Statement
- Scala - yield Keyword
- Scala Classes & Objects
- Scala - Classes & Objects
- Scala - Constructors
- Scala - Auxiliary Constructor
- Scala - Primary Constructor
- Scala - This Keyword
- Scala - Nested Classes
- Scala - Getters and Setters
- Scala - Object Private Fields
- Scala - Singleton Object
- Scala - Companion Objects
- Scala - Creating Executable Programs
- Scala - Stateful Object
- Scala - Enumerations
- Scala - Polymorphism
- Scala - Access Modifiers
- Scala - Apply Method
- Scala - Update Methods
- Scala - UnapplySeq Method
- Scala - Inheritance
- Scala - Extending a Class
- Scala - Method Overloading
- Scala - Method Overriding
- Scala - Generic Classes
- Scala - Generic Functions
- Scala - Superclass Construction
- Scala Methods & Functions
- Scala - Functions
- Scala - Main Methods
- Scala - Functions Call-by-Name
- Scala - Functions with Named Arguments
- Scala - Function with Variable Arguments
- Scala - Recursion Functions
- Scala - Default Parameter Values
- Scala - Functions without Parameters
- Scala - Implicit Parameters
- Scala - Higher-Order Functions
- Scala - Nested Functions
- Scala - Extension Methods
- Scala - Anonymous Functions
- Partially Applied Functions
- Scala - Lazy Val
- Scala - Pure Function
- Scala - Currying Functions
- Scala - Control Abstractions
- Scala - Corecursion
- Scala - Unfold
- Scala - Tail Recursion
- Scala - Infinite Sequences
- Scala - Dynamic Invocation
- Scala - Lambda Expressions
- Scala Collections
- Scala - Collections
- Mutable and Immutable Collections
- Scala - Lists
- Scala - Sets
- Scala - Maps
- Scala - TreeMap
- Scala - SortedMap
- Scala - Tuples
- Scala - Iterators
- Scala - Options
- Scala - Infinite Streams
- Scala - Parallel Collections
- Scala - Algebraic Data Types
- Scala Pattern Matching
- Scala - Pattern Matching
- Scala - Type Patterns
- Scala - Exception Handling
- Scala - Extractors
- Scala - Regular Expressions
- Scala Files I/O
- Scala - Files I/O
- Scala Advanced Concepts
- Scala - Closures
- Scala - Futures
- Scala - Promises
- Scala - Traits
- Scala - Trait Mixins
- Scala - Layered Traits
- Scala - Trait Linearization
- Scala - Sealed Traits
- Scala - Transparent Traits
- Scala - Literal Type Arithmetic
- Scala - Inline keyword
- Scala - Def, Var & Val
- Scala - Dropped Features
- Scala - BDD Testing
Scala - Tail Recursion
This chapter takes you through the concept of tail recursion in Scala programming. Tail recursion optimizes recursive functions where you do not need more stack frames to execute the program. So, you can write efficient recursive functions.
Tail Recursion
Tail recursion is a technique where the recursive call is the last operation in the function. The compiler in Scala can optimize tail-recursive functions to avoid stack overflow errors. So it is as efficient as iterative solutions in Scala.
The recursive call is the final action in the function. So, the Scala compiler can optimize this last recursion. You can reuse the current stack frame for each recursive call.
Syntax
The syntax of tail-recursive function in Scala is -
@scala.annotation.tailrec def functionName(params: Type): ReturnType = { // base case if (condition) baseResult // recursive case else functionName(newParams) }
Example of Tail Recursion
The following example shows defining and using a tail-recursive function in Scala programming -
object Demo { @scala.annotation.tailrec def factorial(n: Int, acc: Int = 1): Int = { if (n <= 1) acc else factorial(n - 1, n * acc) } def main(args: Array[String]): Unit = { println(factorial(5)) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
120
In the example, the factorial method is tail-recursive. The recursive call to factorial is the last operation. The Scala compiler can optimize this recursion.
Advantages of Tail Recursion
Tail recursion is more efficient than normal recursion. Tail-recursion has several advantages and disadvantages. The disadvantage is that if the iterative version of the same program is more efficient, then tail-recursion does not make sense. Because the iterative program does not need any additional stack, but its tail recursive program will always need a stack to store the function calls.
So, its tail recursive program takes up more memory. Therefore, the Scala compiler can convert a tail recursive program into its equivalent iterative program for efficiency purposes.
The advantage of a tail-recursive program is that it can avoid stack overflow. It is easy to convert a tail-recursive program into its equivalent iterative program. Tail recursion may be suitable for processing large data sets.
Example of Non-Tail Recursion
The Tower of Hanoi is a classic example of a problem that uses non-tail recursion. The recursive call is not the last operation. So it is non-tail-recursive.
Syntax
The syntax of non-tail-recursive function in Scala is -
def functionName(params: Type): ReturnType = { // base case if (condition) baseResult // recursive case else { // some operations functionName(newParams) // more operations } }
Example
object Demo { def towerOfHanoi(n: Int, from: String, to: String, aux: String): Unit = { if (n == 1) { println(s"Move disk 1 from $from to $to") } else { towerOfHanoi(n - 1, from, aux, to) println(s"Move disk $n from $from to $to") towerOfHanoi(n - 1, aux, to, from) } } def main(args: Array[String]): Unit = { towerOfHanoi(3, "A", "C", "B") } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
Move disk 1 from A to C Move disk 2 from A to B Move disk 1 from C to B Move disk 3 from A to C Move disk 1 from B to A Move disk 2 from B to C Move disk 1 from A to C
In the example, the towerOfHanoi function is non-tail-recursive. It has more non-recursion action after recursion.
Tail Recursion with Multiple Parameters
You can have multiple parameters in tail-recursive functions. So you can pass more state information through the recursive calls.
Syntax
The syntax of tail-recursive function with multiple parameters is -
@scala.annotation.tailrec def functionName(param1: Type1, param2: Type2, ...): ReturnType = { // base case if (condition) baseResult // recursive case else functionName(newParam1, newParam2, ...) }
Example
Consider the example of tail recursion with multiple parameters in Scala programming -
object Demo { @scala.annotation.tailrec def gcd(a: Int, b: Int): Int = { if (b == 0) a else gcd(b, a % b) } def main(args: Array[String]): Unit = { println(gcd(54, 24)) // Output: 6 } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
6
In the example, the gcd function is tail-recursive. The recursive call to gcd is the last operation. This function also takes two parameters to compute the greatest common divisor.
Tail Recursion with Accumulators
You can use accumulators in tail-recursive functions. You can carry intermediate results through recursive calls.
Syntax
The syntax of tail-recursive function with an accumulator is -
@scala.annotation.tailrec def functionName(param: Type, accumulator: AccType): ReturnType = { // base case if (condition) accumulator // recursive case else functionName(newParam, newAccumulator) }
Example
Consider the example of tail recursion with an accumulator in Scala programming -
object Demo { @scala.annotation.tailrec def sum(n: Int, acc: Int = 0): Int = { if (n <= 0) acc else sum(n - 1, acc + n) } def main(args: Array[String]): Unit = { println(sum(5)) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
15
In the example, the sum function is tail-recursive. The recursive call to sum is the last operation. The function uses an accumulator to carry the intermediate sum through the recursive calls.
Tail Recursion with Nested Functions
You can combine Tail recursion with nested functions. It encapsulates the recursive logic within a helper function. It enhances code organization and readability.
Syntax
The syntax of tail-recursive function with nested functions is -
def outerFunction(params: Type): ReturnType = { @scala.annotation.tailrec def innerFunction(innerParams: Type, accumulator: AccType): ReturnType = { // base case if (condition) accumulator // recursive case else innerFunction(newInnerParams, newAccumulator) } innerFunction(initialInnerParams, initialAccumulator) }
Example
Consider the example of tail recursion with nested functions in Scala programming -
object Demo { def fibonacci(n: Int): Int = { @scala.annotation.tailrec def fibHelper(a: Int, b: Int, count: Int): Int = { if (count == 0) a else fibHelper(b, a + b, count - 1) } fibHelper(0, 1, n) } def main(args: Array[String]): Unit = { println(fibonacci(10)) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
55
In the example, the fibonacci function uses a nested helper function fibHelper. It performs tail-recursive computation of the Fibonacci sequence.
Tail Recursion with Collections
Tail recursion can be effectively used to process collections, like lists. It iterates through the elements recursively.
Syntax
The syntax of tail-recursive function to process collections is -
@scala.annotation.tailrec def functionName(collection: List[Type], accumulator: AccType): ReturnType = { // base case if (collection.isEmpty) accumulator // recursive case else functionName(collection.tail, newAccumulator) }
Example
Consider the example of tail recursion to sum the elements of a list in Scala programming -
object Demo { @scala.annotation.tailrec def sumList(list: List[Int], acc: Int = 0): Int = { if (list.isEmpty) acc else sumList(list.tail, acc + list.head) } def main(args: Array[String]): Unit = { println(sumList(List(1, 2, 3, 4, 5))) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
15
In the example, the sumList function is tail-recursive. The recursive call to sumList is the last operation. The function processes list by summing its elements.
Tail Recursion Summary
- Tail Recursion is recursion where the recursive call is the last operation in the function.
- The Scala compiler optimizes tail-recursive functions by reusing the current stack frame and prevents stack overflow errors.
- The disadvantages of tail-recursion are no benefits of using it. Because its equivalent iterative program does not need any extra stack to store function calls.
- You can use tail recursion with multiple parameters, accumulators, nested functions, and collections for various operations.