Допустим, вы создаете новый список следующим образом:
val n0 = Nil //List()
val n1 = 1 :: n0 //List(1)
val n2 = 2 :: n1 //List(2,1)
val n3 = 3 :: n2 //List(3,2,1)
Если вы создаете такой список, легко заметить, что n3 действительно n2 с добавлением 3 и n2 просто n1 с добавлением 2 и т. д. Поэтому ссылка на 1 является общей для n1 , n2 и n3 , а ссылка на 2 является общей для n2 и
n2 и т. Д.
Вы можете написать это также как:
Cons(3, Cons(2, Cons(1, Nil)))
Это также имеет место в примере из FPinS , когда вы создаете Stream рекурсивно. Вы Stream созданы из вложенных Cons , и каждый подпоток делит элементы со своим родителем. Поэтому, когда создается следующий Stream , он просто оборачивает старый в Cons новым элементом. Но поскольку Stream ленив, все это построение иерархии Cons будет выполнено только в том случае, если вы материализуете его, например, путем вызова toList .
Построение подобного потока обеспечивает также постоянное потребление памяти, поскольку создание следующего потока из предыдущего будет стоить только памяти для нового элемента.
А почему это не так с unfold
? Потому что он строит Stream "наоборот". Итак, это выглядит так:
Cons(x, next_iteration_of_unfold) //1st iteration
Cons(x, Cons(x, next_iteration_of_unfold)) //2nd iteration
Cons(x, Cons(x, Cons(x, next_iteration_of_unfold))) //3rd iteration, etc.
Итак, как вы видите, ничто не может быть передано.
EDIT:
Чтобы увидеть, как выглядит материализованный поток, вызовите take
в окончательной реализации в книге и добавьте toString
к Cons
:
override def toString: String = s"Cons(${h()}, ${tl()})"
А потом:
Stream.ones.take(3)
Вы увидите:
Cons(1, Cons(1, Cons(1, Empty)))