Бесконечный список в haskell в сочетании с fold* не вычисляется
допустим, я хочу получить отсортированный бесконечный список всех primepowers до степени n
.
у меня есть функция для объединения двух отсортированных списков и функция, которая дает мне простые числа.
merge :: Ord t => [t] -> [t] -> [t]
merge (x:xs) (y:ys)
| (x <= y) = x : merge xs (y:ys)
| otherwise = y : merge (x:xs) ys
merge xs [] = xs
merge [] ys = ys
primes :: [Integer]
primes = sieve [2..]
where
sieve [] = []
sieve (p:xs) = p : sieve (filter (x -> x `mod` p /= 0) xs)
у меня есть две версии :
primepowers :: Integer -> [Integer]
primepowers n = foldr (merge) [] (listOfPrimepowers n)
-- terminating
listOfPrimepowers' n = map(x -> (map(y -> y ^ x) primes)) [1..n]
-- non terminating
listOfPrimepowers'' n = map(x -> (map(y -> x ^ y) [1..n])) primes
один обеспечивает правильный результат, а другой-нет. Единственная разница в том, что первая версия отображает primepowers таким образом, как [[2,3,5,7, ...],[4,9,25,...]]
и вторая версия отображает primepowers как [[2,4,8],[3,9,27],[5,25,125], ...]
. Видите ли, бесконечность находится на другом уровне списка.
у вас есть объяснение, почему 2-я функция не производит никакого вывода?
4 ответов
это вызвано foldr merge
приходится просматривать все списки, чтобы найти минимальный элемент во главе одного из списков. Если foldr merge
дается бесконечный список конечных списков, foldr merge
невозможно вычислить первый элемент списка - он продолжает искать минимальный элемент остальной части списка списков, прежде чем он сможет сравнить его с первым элементом первого списка - 2
. С другой стороны, если foldr merge
дается конечный список бесконечных списков,foldr merge
можно определить первый элемент Объединенного списка, и переходит к следующему. Таким образом, вы можете создать произвольное количество элементов в первом случае, но не один во втором случае.
давайте расширим foldr merge []
:
foldr merge [] (xs0:xs1:xs2:...:[]) =
merge xs0 (merge xs1 (merge xs2 (merge xs3 ... [])))
очевидно, что если (xs0:xs1:xs2:...:[])
бесконечно, "вложенные" вызовы для слияния образуют бесконечную цепочку. Но как насчет того, что Хаскелл "ленив"? primes
также определяется в терминах себя, но он производит выход? Ну, вообще-то есть правило foldr
: он может производить вывод для бесконечных списков, только если функция передана в foldr
не является строгим во втором аргументе-т. е. иногда он может производить вывод без необходимости оценивать результат foldr
для остальной части списка.
на merge
pattern-соответствует второму аргументу-только это может привести к не-завершению - и использует строгую функцию <=
, так что merge
строг во втором аргументе, и бесконечная цепь должна будет оценить первый элемент каждого списка перед верхним уровнем merge
смогите произвести любой результат.
, потому что merge
строг, а потому merge
ассоциативно, вы можете - и должны - использовать foldl'
вместо foldr
объединить конечный список бесконечных списков.
вы можете сделать оба варианта работы, достаточно легко. Включили технику, стоит знать.
ваш код эквивалентен
primepowers n =
foldr merge []
-- terminating:
-- [[p^k | p <- primes] | k <- [1..n]] -- n infinite lists
-- non terminating:
[[p^k | k <- [1..n]] | p <- primes] -- infinite list of n-length lists
imerge (x:xs) ys = x : merge xs ys
(как видно из кода Ричарда Берда в статья мелиссы О'Нил). Теперь оба варианта будут просто работать. imerge
не является строгим в своем 2-м аргументе и, естественно, используется с foldr
, даже с конечными списками.
используя foldl
вместо foldr
просто неправильно со 2-м вариантом, потому что foldl
в бесконечном списке не заканчивается.
используя foldl
С 1-го вариант, хотя и возможный (поскольку сам список конечен), здесь неоптимален: он разместит более часто производящие потоки в нижней части общей цепочки слияний, т. е. он будет работать медленнее, чем тот, с foldr
.
Читайте также: складки на Википедии.
вторая версия не дает вам никаких выходных данных, потому что вход представляет собой бесконечный список списков. Если подумать,foldr merge []
создает отсортированный список из списка списков, поэтому главным элементом результирующего списка будет минимум всех главных элементов списков. Естественно, что взятие минимума бесконечного списка не заканчивается, поэтому функция даже не доходит до точки, когда первый элемент результата становится доступным.
ни одна из ваших функций не будет прекращена
первое, что primes
- это бесконечный список. Это означает, что понимание программы обитает в ее ленивой оценке
для первой функции
primepowers :: Integer -> [Integer]
primepowers n = foldr (merge) [] (listOfPrimepowers n)
listOfPrimepowers n = map (\x -> (map (\y -> y ^ x) primes)) [1..n]
не будет прекращено, потому что. Даже если внешняя карта применяется к конечному Списку [1..n] каждый \x
потреблять внутренним map
применяется в бесконечном списке primes
.
вторая функция
primepowers :: Integer -> [Integer]
primepowers n = foldl (merge) [] (listOfPrimepowers n)
listOfPrimepowers n = map(\x -> (map(\y -> x ^ y) [1..n])) primes
не завершится, потому что внешний map
применяется непосредственно в бесконечном списке primes