Ваше «Потому что» не раскрывает всей истории. Вы обрезаете списки на уровне «история на данный момент» и нетерпеливо оцениваете, а затем задаетесь вопросом, откуда взялось остальное. Это не совсем так, чтобы понять, что на самом деле происходит, так что хороший вопрос.
Что вычисляется, когда вы даете определение
fibs = 0 : 1 : zipWith (+) fibs (drop 1 fibs)
? Очень мало. Вычисления начнутся, как только вы начнете использовать список. Ленивые вычисления выполняются только по запросу.
Какой спрос? Вы можете спросить: "Вы []
или x : xs
?" и если это последнее, вы получите представление о деталях.
Когда мы задаем этот вопрос fibs
, мы получаем, что
fibs = x0 : xs0
x0 = 0
xs0 = 1 : zipWith (+) fibs (drop 1 fibs)
но это означает (заменяя fibs
, а затем x0
)
xs0 = 1 : zipWith (+) (0 : xs0) (drop 1 (0 : xs0))
и когда мы снова спрашиваем, мы получаем, что
xs0 = x1 : xs1
x1 = 1
xs1 = zipWith (+) (0 : xs0) (drop 1 (0 : xs0))
so
xs1 = zipWith (+) (0 : 1 : xs1) (drop 1 (0 : 1 : xs1))
но теперь становится интересно, потому что нам нужно поработать. Достаточно работы, чтобы ответить на вопрос, не так ли? Когда мы смотрим на xs1
, мы форсируем zipWith
, который вынуждает drop
.
xs1 = zipWith (+) (0 : 1 : xs1) (drop 1 (0 : 1 : xs1))
= zipWith (+) (0 : 1 : xs1) (1 : xs1)
= (0 + 1) : zipWith (+) (1 : xs1) xs1
so
xs1 = x2 : xs2
x2 = 0 + 1 = 1
xs2 = zipWith (+) (1 : xs1) xs1
= zipWith (+) (1 : 1 : xs2) (1 : xs2)
Видеть? Мы утверждали, что нам известны первые два элемента одного заархивированного списка и первый элемент другого. Это означает, что мы сможем доставить следующий вывод и обновить наш «буфер». Когда мы смотрим на xs2
, мы получаем
xs2 = zipWith (+) (1 : 1 : xs2) (1 : xs2)
= (1 + 1) : zipWith (1 : xs2) xs2
xs2 = x3 : xs3
x3 = 1 + 1 = 2
xs3 = zipWith (1 : xs2) xs2
= zipWith (1 : 2 : xs3) (2 : xs3)
и мы снова в порядке!
Каждый раз, когда нам требуется следующий элемент, мы также делаем шаг дальше от zipWith
исчерпания элементов, что также хорошо, как раз в самый последний момент.
Ни одна из дисциплин, которая заставляет ценности проявляться в самый последний момент, не выражается в типах. На данный момент программисты должны убедиться, что хорошо типизированные программы не выйдут из строя из-за нехватки данных при поступлении запроса. (У меня есть планы что-то с этим сделать, но я постараюсь не отвлекаться.)
Ключ в том, что ленивое вычисление «по запросу» означает, что нам не нужно усекать списки до тех элементов, которые мы можем видеть при запуске процесса. Нам просто нужно знать, что мы всегда можем сделать следующий шаг.
person
pigworker
schedule
04.10.2015
0 : 1 : [1] : [1,2] : [1,2,3]
? - person dfeuer   schedule 04.10.20150 : 1 : zipWith (+) [0,1] [1]
дает[1]
, аzipWith (+) [0,1,1] [1,1]
дает[1,2]
и т. Д. - person dopatraman   schedule 04.10.2015[0,1,1,...]
с[1,1,...]
. И это тоже не список промежуточных списков. - person dfeuer   schedule 04.10.2015