Как фильтровать бесконечный список в Haskell

Возможный дубликат:
Конечное понимание бесконечного списка

Я не могу понять, почему ghci не может правильно вычислить этот код?

[x | x <- [1..], x < 1000]

Ghci просто останавливается на последнем номере, и мне нужно прервать этот процесс в командной строке, чтобы вернуться в нормальное состояние. Что не так? Я ожидаю, что этот код должен работать из-за ленивой оценки haskell.

Ответ 1

[x | x <- [1..], x < 1000] эквивалентен filter (< 1000) [1..]; вы хотите вместо takeWhile (< 1000) [1..].

Итак, какая разница между filter и takeWhile?

Хорошо, если вы попытаетесь оценить весь результат --- и что то, что делает ghci, чтобы его распечатать --- тогда filter проверит каждый элемент в списке ввода, чтобы определить, должно ли оно быть в выходном списке. После первой тысячи элементов? Он проводит тестирование. filter не знает, что он не встретится внезапно ..., 12345, 12346, -7, 12348, ....

Другим способом взглянуть на это является то, что filter может только сказать "конец вывода заканчивается здесь", как только он достигнет конца списка ввода. Если вы подаете ему бесконечный список, он никогда не может быть уверен, что он сгенерировал все элементы выходного списка. Таким образом, он будет виден.

takeWhile, с другой стороны, останавливает и завершает свой выходной список, как только он достигает элемента, который не выполняет условие.

Ответ 2

Вы просто задали для каждого номера менее 1000 в списке. Вы знаете, что список отсортирован, но ваш алгоритм не использует это. Компилятор не понимает автоматически, что вы работаете с отсортированными данными, и не можете сделать вывод о том, что, как только вы увидите число, которое составляет не менее 1000, вы больше никогда не сможете увидеть тот, который меньше этого.

Как отмечали другие, takeWhile (< 1000) [1..] использует ваши знания о том, насколько особым ваш список является, в частности, останавливая рассмотрение этого списка после того, как предикат потерпел неудачу для элемента (в этом случае, если число, которое составляет не менее 1000). Обратите внимание, что это оптимизация, потому что [1..] не является "просто списком"; это список со специальными свойствами (в частности, он отсортирован).

Ответ 3

Поскольку вы создаете бесконечное число с [1..], каждый элемент этого списка проверяется с вашим условием x < 1000. Это также означает, что каждый элемент, превышающий 1000 из этого списка, проверяется с помощью этого условия.

Но вы можете написать свою функцию следующим образом:

myFilteredList :: [Int] -> [Int]
myFilteredList [] = []
myFilteredList (x:xs) = if x < 1000 then x: myFilteredList xs else []

Для более общего поведения вы также можете принять условие как аргумент.

myFilteredList :: (a -> Bool) -> [a] -> [a]
myFilteredList cond [] = []
myFilteredList cond (x:xs) = if cond x then x: myFilteredList cond xs else []

myFilteredList (< 1000) [1..]

И это именно то, что делает предопределенная функция takeWhile. Он принимает условие (a -> Bool) и список [a] в качестве аргументов и возвращает префикс списка, который соответствует условию. Как и в определении myFilteredList, функция завершается, если она обрабатывает весь список или достигает первого элемента в списке, который не содержит условия.

Ответ 4

Проверка x < 1000 используется для определения того, что включать в отфильтрованный список, а не для остановки оценки. Другими словами, вы предоставляете ghci бесконечный список, и он будет делать x < 1000 для каждого элемента списка. Если вы хотите взять элементы, пока предикат не будет True, используйте takeWhile. Не имеет значения, что Haskell ленив, отфильтрованный список оценивается, потому что ghci печатает его. Если вы хотите этого избежать, используйте let

let l = [x | x <- [1..], x < 1000]