Бесконечный список в 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

"естественно, взятие минимума бесконечного списка не заканчивается"

вот, бесконечный список primes и уже отсортированный в порядке возрастания его элементов. Итак, главы списки [p^k | k <- [1..n]] сортируются и увеличиваются, и сами списки сортируются и увеличиваются тоже. основываясь на этом знании в одиночку мы можем сразу произвести головной элемент Объединенных потоков-минимум бесконечного списка глав всех этих списков-за O(1) время, фактически не проверяя любой из них, за исключением самого первого (который и есть ответ).

ваша проблема решается с помощью следующей функции вместо merge, в foldr выражение:

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