Почему этот код Haskell успешно работает с бесконечными списками?

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

myAny :: (a -> Bool) -> [a] -> Bool
myAny p list = foldr step False list
   where
      step item acc = p item || acc

мое понимание foldr заключается в том, что он будет проходить через каждый элемент в списке (и, возможно, это понимание незавершенный.) Если это так, то не имеет значения, как сформулирована функция "шаг"... код не должен обрабатывать бесконечные циклы.

однако, следующие работы:

*Main Data.List> myAny even [1..]
True

пожалуйста, помогите мне понять: почему??

4 ответов


давайте сделаем небольшой след в наших головах о том, как Хаскелл оценит ваше выражение. Подставляя equals для equals в каждой строке, выражение довольно быстро вычисляет True:

myAny even [1..]
foldr step False [1..]
step 1 (foldr step False [2..])
even 1 || (foldr step False [2..])
False  || (foldr step False [2..])
foldr step False [2..]
step 2 (foldr step False [3..])
even 2 || (foldr step False [3..])
True   || (foldr step false [3..])
True

это работает, потому что acc передается как unevaluated thunk (ленивая оценка), но также и потому, что || функция строга в своем первый


мое понимание foldr заключается в том, что это будет цикл через каждый элемент список (и, возможно, это понимание неполный.)

foldr (в отличие от foldl) не нужно перебирать каждый элемент списка. Поучительно посмотреть на то, как foldr определяется.

foldr f z []     = z
foldr f z (x:xs) = f x (foldr f z xs)

при вызове foldr оценивается, это заставляет оценку вызова функции f. Но обратите внимание, как рекурсивный вызов foldr is встроенный в аргумент функции f. Этот рекурсивный вызов не оценивается, если f не оценивает свой второй аргумент.


ключевым моментом здесь является то, что Haskell является нестрогим языком. "Нестрогий" означает, что он допускает нестрогие функции, что, в свою очередь, означает, что параметры функции не могут быть полностью оценены до их использования. Это, очевидно, позволяет лениво оценивать, что является "методом задержки вычисления до тех пор, пока результат не потребуется".

Начнем с эта статья Wiki


Я не знаю Haskell, но я подозреваю, что в вашем случае он работает из-за ленивой оценки. Поскольку он позволяет работать со списком бесконечно долго, при доступе к нему он вычислит результат по мере необходимости.

см.http://en.wikipedia.org/wiki/Lazy_evaluation