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".
Ссылки: