LazyList

Коллекции Scala также включают LazyList, который представляет собой ленивый неизменяемый связанный список. Он называется «ленивым» — или нестрогим — потому что вычисляет свои элементы только тогда, когда они необходимы. Поэтому ленивый список может быть бесконечно длинным. Обрабатываются только те элементы, которые запрашиваются. В остальном у ленивых списков те же параметры производительности, что и обычных.

Если списки создаются с помощью оператора ::, то ленивые списки создаются схожей операцией #::. Вот простой пример ленивого списка с целыми числами 1, 2 и 3:

val lazyList = 1 #:: 2 #:: 3 #:: LazyList.empty
// lazyList: LazyList[Int] = LazyList(1, 2, 3)
lazyList.toString
// res0: String = "LazyList(<not computed>)"

На первом месте в этом ленивом списке - 1, затем - 2 и 3. Но ни один из элементов не выводится, потому что список еще не вычислен! Ленивые списки задуманы обрабатываться лениво, поэтому метод toString не выводит элементов, не производя дополнительные вычисления.

Ещё примеры:

val x = LazyList.range(1, Int.MaxValue)
x.take(1)    
x.take(5)    
x.map(_ + 1) 

LazyList начинает вычислять свои элементы только при вызове некоторых методов, например, foreach:

x.take(1).foreach(println)
// 1
Последовательность Фибоначчи

Пример ленивого списка, содержащего последовательность Фибоначчи.

def fibFrom(a: Int, b: Int): LazyList[Int] = a #:: fibFrom(b, a + b)

Эта функция обманчиво проста. Первый элемент очевидно a, остальная часть - это последовательность Фибоначчи, начинающаяся с b, за которой следует a + b. Сложность состоит в том, как вычислить эту последовательность, не вызывая бесконечной рекурсии. Если бы функция использовала :: вместо #::, то каждый вызов функции приводил бы к очередному вызову, вызывая тем самым бесконечную рекурсию. Но так как используется #::, то вычисление правой части не производится до тех пор, пока она не будет запрошена.

Ниже приведены первые элементы последовательности Фибоначчи, начиная с двух едениц:

val fibs = fibFrom(1, 1).take(7)
// fibs: LazyList[Int] = LazyList(1, 1, 2, 3, 5, 8, 13)
fibs.toList
// res5: List[Int] = List(1, 1, 2, 3, 5, 8, 13)

Детали

Для получения дополнительной информации об использовании, преимуществах и недостатках строгих и нестрогих (ленивых) коллекций см. обсуждение "строгих" и "нестрогих" коллекций на странице "Архитектура Scala 2.13’s Collections".


Ссылки: