В Haskell, какая разница между использованием takeWhile или использованием "регулярного" неравенства в этом понимании списка?

Я пытаюсь выучить меня Haskell (для большого блага), и одна из многих других вещей, которые я делаю, пытается решить некоторые проблемы Project Euler, когда я собираюсь проверить свой характер.

Выполняя некоторые из проблем, связанных с Фибоначчи, я наткнулся и начал играть с рекурсивной версией бесконечного списка последовательности Фибоначчи:

fibs = 1 : 2 : zipWith (+) fibs (tail fibs)

Для одной из проблем PE мне нужно было извлечь подпоследовательность даже чисел Фибоначчи менее 4 000 000. Я решил сделать это со списком, и в моей игре с кодом я наткнулся на то, что я не совсем понимаю; Я предполагаю, что это мое слабое понимание на ленивой схеме оценки Haskell, которая усложняет ситуацию.

Следующее понимание прекрасно работает:

[x | x <- takeWhile (<= 4000000) fibs, even x]

Следующее понимание вращается навсегда; поэтому я прошел через и вернул выход в stdout, и пока он останавливается в правильном месте, он просто продолжает оценивать рекурсивно определенный список навсегда, не заканчивая после достижения закрытого значения; что последний элемент в списке печатается с запятой, но нет других элементов списка или закрывающей квадратной скобки:

[x | x <- fibs, x <= 4000000, even x]

Итак, что же такое секретный соус, используемый различными функциями, которые хорошо играют с бесконечными списками?

Ответ 1

Функция takeWhile сохраняет элементы входного списка до тех пор, пока не достигнет первого элемента, который не удовлетворяет предикату, а затем он остановится. Пока существует хотя бы один элемент, который не удовлетворяет предикату, takeWhile превращает бесконечные списки в конечные списки.

Ваше первое выражение говорит

Продолжайте принимать элементы этого бесконечного списка, пока не найдете более 4 000 000, а затем остановитесь. Включите каждый элемент в выход, если он даже.

Второе выражение говорит

Продолжайте принимать элементы этого бесконечного списка. Включите каждый элемент в выход, если он меньше или равен 4,000,000, и даже.

Когда вы наблюдаете вывод, который вешает навсегда, функция на деле вырабатывает больше чисел фибоначчи и проверяет, меньше ли они или равна 4 000 000. Ни один из них, поэтому ничто не печатается на экране, но функция не знает, что она не будет сталкиваться с небольшим числом немного дальше по списку, поэтому она должна продолжать проверять.