Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lazy List

A LazyList is a lazy, immutable collection that represents a sequence of elements. Elements of a LazyList are computed only when needed, which allows for efficient handling of potentially infinite sequences.

Creating LazyLists

You can create a LazyList using the LazyList companion object.

val lazyList = LazyList(1, 2, 3, 4, 5)

You can also create an infinite LazyList using the LazyList.cons method or the #:: operator.

// Infinite LazyList of natural numbers
def from(n: Int): LazyList[Int] = n #:: from(n + 1)
val naturals = from(1)

Accessing Elements

Head and Tail

  • Head: The first element of the LazyList.
  • Tail: The rest of the LazyList (which is lazily evaluated).
val lazyList = LazyList(1, 2, 3, 4, 5)

println(lazyList.head) // 1
println(lazyList.tail) // LazyList(<not computed>)

Taking Elements

You can take the first n elements of a LazyList using the take method.

val naturals = from(1)
val firstFive = naturals.take(5)
println(firstFive.toList) // List(1, 2, 3, 4, 5)

Basic Operations on LazyLists

Mapping

The map method transforms each element of the LazyList using a given function.

val lazyList = LazyList(1, 2, 3, 4, 5)
val squaredLazyList = lazyList.map(n => n * n)
println(squaredLazyList.take(5).toList) // List(1, 4, 9, 16, 25)

Filtering

The filter method selects elements of the LazyList that satisfy a predicate.

val lazyList = LazyList(1, 2, 3, 4, 5)
val evenLazyList = lazyList.filter(_ % 2 == 0)
println(evenLazyList.take(2).toList) // List(2, 4)

Reducing

The reduce method combines the elements of the LazyList using a binary operation.

val lazyList = LazyList(1, 2, 3, 4, 5)
val sum = lazyList.reduce(_ + _)
println(sum) // 15

Folding

The fold method combines elements of the LazyList using a binary operation and a starting value.

val lazyList = LazyList(1, 2, 3, 4, 5)
val sum = lazyList.fold(0)(_ + _)
println(sum) // 15

Infinite LazyLists

One of the key advantages of LazyLists is the ability to handle infinite sequences.

val naturals = from(1)
println(naturals.take(10).toList) // List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

You can create more complex infinite sequences.

val fibs: LazyList[Int] = 0 #:: 1 #:: fibs.zip(fibs.tail).map { case (a, b) => a + b }
println(fibs.take(10).toList) // List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)

Advanced Operations

Lazy Evaluation

LazyLists are lazily evaluated, meaning elements are computed only when accessed.

val lazyList = LazyList(1, 2, 3, 4, 5)
println(lazyList) // LazyList(<not computed>)
println(lazyList.force) // LazyList(1, 2, 3, 4, 5)

Pattern Matching

You can use pattern matching to deconstruct LazyLists.

val lazyList = LazyList(1, 2, 3, 4, 5)

lazyList match {
  case LazyList() => println("The LazyList is empty")
  case head #:: tail => println(s"The head is $head and the tail is $tail")
}
// Output: The head is 1 and the tail is LazyList(<not computed>)

Conversions

You can convert LazyLists to other collection types, such as lists or arrays.

val lazyList = LazyList(1, 2, 3, 4, 5)

val list = lazyList.toList
println(list) // List(1, 2, 3, 4, 5)

val array = lazyList.toArray
println(array.mkString(", ")) // 1, 2, 3, 4, 5