Scala - Infinite Sequences



This chapter takes you through the concept of infinite sequences in Scala programming. Infinite sequences are collections that can represent an unbounded number of elements that are generated on-demand. These are also known as streams. You can work with unbounded data sets with lazy evaluation without running out of memory.

Infinite Sequences

Infinite sequences can have an infinite number of elements. These sequences are evaluated lazily and can potentially. So, you can work with large and unbounded data sets without loading everything in the memory because of lazy evaluation.

Infinite sequence is a collection that can have unlimited number of elements. It evaluates its elements lazily. So, elements are only computed when these are accessed.

Syntax

The syntax of infinite sequence in Scala typically involves the Stream and LazyList class -

val infiniteSequence = Stream.from(0)
// or
val infiniteSequence = LazyList.from(0)

Example of Infinite Sequences

The following example shows defining and using an infinite sequence in Scala programming -

object Demo {
   def main(args: Array[String]): Unit = {
      val infiniteSequence = LazyList.from(1)
      val firstTen = infiniteSequence.take(10).toList
      println(firstTen) 
   }
}

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(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

In the example, the LazyList.from(1) creates an infinite sequence of natural numbers starting from 1. This method retrieves the first ten elements and toList converts these into lists for printing.

Advantages of Infinite Sequences

There are various advantages of infinite sequences. These are memory-efficient because these only evaluate elements when these are needed. You can do that using lazy evaluation without consuming a large amount of memory. You can use various functional operations like, map, filter, take, and drop. You can write more concise and readable code.

Creating Infinite Sequences

You can create infinite sequences in Scala using various methods, like LazyList and Stream.

Using LazyList

You can use LazyList class to create infinite sequences. The syntax is -

val infiniteSequence = LazyList.iterate(0)(_ + 1)

Example

object Demo {
   def main(args: Array[String]): Unit = {
      val infiniteSequence = LazyList.iterate(0)(_ + 1)
      val firstTen = infiniteSequence.take(10).toList
      println(firstTen) 
   }
}

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 LazyList.iterate(0)(_ + 1) creates an infinite sequence of integers starting from 0. This method retrieves the first ten elements and toList converts these to a list for printing.

Using Stream

You can use Stream class as an alternative to LazyList for creating infinite sequences. The syntax is -

val infiniteStream = Stream.from(0)

Example

object Demo {
   def main(args: Array[String]): Unit = {
      val infiniteStream = Stream.from(0)
      val firstTen = infiniteStream.take(10).toList
      println(firstTen) 
   }
}

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 Stream.from(0) creates an infinite stream of integers starting from 0. This method retrieves the first ten elements and toList converts them to a list for printing.

Infinite Sequences with Custom Generators

You can create infinite sequences with custom generators using the iterate method. You can write your own generator function.

Syntax

The syntax of infinite sequence with a custom generator is -

val infiniteSequence = LazyList.iterate(initialValue)(generatorFunction)

Example

Consider the example of generating an infinite sequence of Fibonacci numbers in Scala programming -

object Demo {
   def main(args: Array[String]): Unit = {
      lazy val fibonacci: LazyList[BigInt] = BigInt(0) #:: BigInt(1) #:: 
      fibonacci.zip(fibonacci.tail).map { case (a, b) => a + b }
      val firstTen = fibonacci.take(10).toList
      println(firstTen) 
   }
}

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 fibonacci sequence is defined using a custom generator function. It creates an infinite sequence of Fibonacci numbers. The fibonacci sum is sum of two previous numbers.

Processing Infinite Sequences

You can process infinite sequences using various functional operations like map, filter, take, and drop.

Syntax

The syntax for processing an infinite sequence is -

val processedSequence = infiniteSequence.map(transformation).filter(condition).take(n)

Example

Consider the example of processing an infinite sequence to get the first ten even numbers in Scala programming -

object Demo {
   def main(args: Array[String]): Unit = {
      val infiniteSequence = LazyList.from(1)
      val firstTenEvens = infiniteSequence.filter(_ % 2 == 0).take(10).toList
      println(firstTenEvens) 
   }
}

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(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

In the example, the filter method processes the infinite sequence to get only the even numbers. The method retrieves the first ten elements.

Combining Infinite Sequences

You can combine infinite sequences using various functional operations like zip, concat, and flatMap.

Syntax

The syntax for combining infinite sequences is -

val combinedSequence = infiniteSequence1.zip(infiniteSequence2)

Example

Consider the example of combining two infinite sequences to create pairs of numbers in Scala programming

object Demo {
   def main(args: Array[String]): Unit = {
      val sequence1 = LazyList.from(1)
      val sequence2 = LazyList.from(10)
      val combinedSequence = sequence1.zip(sequence2).take(10).toList
      println(combinedSequence)  
   }
}

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((1,10), (2,11), (3,12), (4,13), (5,14), (6,15), (7,16), (8,17), (9,18), (10,19))

In the example, the zip method combines two infinite sequences to create pairs of numbers. This method retrieves the first ten pairs.

Infinite Sequences Summary

  • Infinite sequences are collections that are evaluated lazily and can have an infinite number of elements.
  • Infinite sequences are memory-efficient. So, you can work with large and unbounded data sets without consuming a large amount of memory.
  • Infinite sequences can be created using LazyList and Stream. You can process these various functional operations like map, filter, take, and drop.
  • You can create infinite sequences with custom generators and process these efficiently. You can combine them using functional operations.
Advertisements