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.