
- 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 - Unfold
This chapter takes you through the concept of unfold in Scala programming. The unfold function is used to construct sequences and collections from a seed value. The function is applied repeatedly which produces the next state and value. It is the dual operation of fold. Fold reduces collection to a single value and unfold creates collection from a single value.
Unfold
The unfold function can be used to create collections, like, lists and streams. It generates subsequent elements based on a function that defines how to compute the next element and state. It is used to generate infinite sequences and continues until a termination condition is met.
Unfold is a technique of creating collection repeatedly. It generates the next state and value (from the previous pair) until a termination condition is met.
Syntax
The syntax of unfold function is -
def unfold[S, A](init: S)(f: S => Option[(A, S)]): List[A] = f(init) match { case Some((a, s)) => a :: unfold(s)(f) case None => Nil }
Example of Unfold
The following example shows defining and using an unfold function in Scala programming -
object Demo { def unfold[S, A](init: S)(f: S => Option[(A, S)]): List[A] = f(init) match { case Some((a, s)) => a :: unfold(s)(f) case None => Nil } def main(args: Array[String]): Unit = { val list = unfold(0) { n => if (n < 10) Some((n, n + 1)) else None } println(list) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
In the example, the unfold function generates a list of integers from 0 to 9 repeatedly.
Advantages of Unfold
You can generate collections based on custom rules for computing the next state and value. So it is used in various use cases. It promotes modular and reusable code. The unfold function can be used to generate infinite sequences. So it is for efficient and on-demand data generation.
Unfold with Streams
Streams are used to implement unfold functions. Streams are lazy lists, where elements are computed only when these are accessed.
Syntax
The syntax of unfold with streams is -
def unfoldStream[S, A](init: S)(f: S => Option[(A, S)]): Stream[A] = f(init) match { case Some((a, s)) => a #:: unfoldStream(s)(f) case None => Stream.empty }
Example
Consider the example of generating an infinite stream of Fibonacci numbers using unfold in Scala programming -
object Demo { def unfoldStream[S, A](init: S)(f: S => Option[(A, S)]): Stream[A] = f(init) match { case Some((a, s)) => a #:: unfoldStream(s)(f) case None => Stream.empty } def main(args: Array[String]): Unit = { val fibs = unfoldStream((0, 1)) { case (a, b) => Some((a, (b, a + b))) } println(fibs.take(10).toList) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
In the example, the unfoldStream function generates an infinite stream of Fibonacci numbers by repeatedly. It applies the provided function to compute the next value and state.
Unfold with Trees
You can also use unfold to generate tree-like structures, where each node is generated step by step.
Syntax
The syntax of unfold with trees is -
case class Tree[A](value: A, children: List[Tree[A]]) def unfoldTree[S, A](init: S)(f: S => Option[(A, List[S])]): Tree[A] = f(init) match { case Some((a, ss)) => Tree(a, ss.map(unfoldTree(_)(f))) case None => throw new Exception("Unfold terminated unexpectedly") }
Example
Consider the example of generating an infinite binary tree using unfold in Scala programming -
case class Tree[A](value: A, left: () => Tree[A], right: () => Tree[A]) object Demo { def unfoldTree[S, A](init: S)(f: S => Option[(A, (S, S))]): Tree[A] = f(init) match { case Some((a, (l, r))) => lazy val leftTree = unfoldTree(l)(f) lazy val rightTree = unfoldTree(r)(f) Tree(a, () => leftTree, () => rightTree) case None => throw new Exception("Unfold terminated unexpectedly") } def main(args: Array[String]): Unit = { val tree = unfoldTree(1) { n => Some((n, (n * 2, n * 2 + 1))) } println(tree.left().left().value) println(tree.right().right().value) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
4 7
In the example, the unfoldTree function generates an infinite binary tree.
Unfold with Custom Collections
You can define your own collection types and use unfold to generate them.
Syntax
The syntax of unfold with custom collections is -
class CustomCollection[A](elements: List[A]) { def unfold[S](init: S)(f: S => Option[(A, S)]): CustomCollection[A] = { def loop(s: S): List[A] = f(s) match { case Some((a, nextS)) => a :: loop(nextS) case None => Nil } new CustomCollection(loop(init)) } }
Example
Consider the example of generating a custom collection of even numbers using unfold in Scala programming -
class CustomCollection[A](val elements: List[A]) { def unfold[S](init: S)(f: S => Option[(A, S)]): CustomCollection[A] = { def loop(s: S): List[A] = f(s) match { case Some((a, nextS)) => a :: loop(nextS) case None => Nil } new CustomCollection(loop(init)) } override def toString: String = elements.toString() } object Demo { def main(args: Array[String]): Unit = { val collection = new CustomCollection[Int](Nil).unfold(0) { n => if (n <= 20) Some((n, n + 2)) else None } println(collection) } }
Save the above program in Demo.scala. Use the following commands to compile and execute this program.
Command
> scalac Demo.scala > scala Demo
Output
CustomCollection(List(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20))
In the example, the unfold function generates a custom collection of even numbers up to 20.
Unfold Summary
- Unfold is a technique for generating collections from a seed value. It repeatedly applies a function that produces the next state and value. These provide flexibility and modularity
- You can unfold to generate infinite sequences, streams, trees, and custom collections.
- You can generate complex data structures in a clean and efficient manner in Scala programming.