Почему идея генерации простых чисел в Haskell не работает?

Я пытаюсь использовать алгоритм «Решето Эратосфена» в Haskell для генерации потока простых чисел, но код, похоже, не работает. Это основная идея.

Основная идея взята из наших лекций по функциональному программированию, которые, в свою очередь, ссылаются на статью Мелиссы О'Нил «Подлинное решето Эратосфена», JFC, 2009, в которой обсуждается, что на самом деле представляет собой решето Эратосфена в Haskell.

union :: Ord a => [a] -> [a] -> [a]
union xs@(x:txs) ys@(y:tys)
    | x < y = x:union txs ys
    | x > y = y:union xs tys
    -- se sono uguali, ne scrivo uno solo
    | otherwise = x:union txs tys

minus :: Ord a => [a] -> [a] -> [a]
minus xs@(x:txs) ys@(y:tys)
    | x < y = x : minus txs ys
    | x > y = y : minus xs tys
    | otherwise = x : minus xs ys    

unionP :: Ord a => [a] -> [a] -> [a]
unionP (x:xs) ys = x:union xs ys -- poi si va con union

unionAll :: [[Integer]] -> [Integer]
unionAll = foldr1 unionP

primes :: [Integer]
primes = 2:[3..] `minus` composites 
    where composites = unionAll [map (p*) [p..] | p <-primes]

-- Test
firstPrimes = take 15 primes    

Вот что произошло на самом деле, дальше этого дело не идет:

ghci> firstPrimes [2

Поэтому мне пришлось прервать процесс:

ghci> firstPrimes [2^CInterrupted. 
ghci>

Пожалуйста, всегда используйте подписи типов. Кроме того, 2 : [3..]`minus`composited намного понятнее, чем версия с большим количеством пробелов вокруг оператора с более высоким приоритетом.

leftaroundabout 29.04.2024 19:25

Похоже, что unionAll используется для получения бесконечного количества отсортированных списков и создания отсортированного списка их объединения. Я не понимаю, как это может работать, даже с ленью. Чтобы выдать элемент x, функция должна быть уверена, что первый элемент всех списков больше x, что невозможно проверить за конечное время. Возможно, вам нужно более сильное предварительное условие (например, список sorted() из бесконечного числа отсортированных списков, где () использует какой-то особый порядок) и более умное unionAll (foldr1 недостаточно, AFAICS).

chi 29.04.2024 19:37
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
109
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, ваша реализация minus неверна. В статье О'Нила используются:

(x:xs) `minus` (y:ys) | x < y = x:(xs `minus` (y:ys))
                      | x == y = xs `minus` ys
                      | x > y = (x:xs) `minus` ys

Преобразуя в ваши обозначения, это должно выглядеть так:

minus :: Ord a => [a] -> [a] -> [a]
minus xs@(x:txs) ys@(y:tys)
    | x < y = x : minus txs ys
    | x > y = minus xs tys
    | otherwise = minus txs ys

Во-вторых, в определении union в статье О'Нила используется foldr, а не foldr1, поэтому это эквивалентно:

unionAll = foldr unionP []

вместо:

unionAll = foldr1 unionP

Это может показаться не таким уж большим делом, но простые определения этих функций выглядят так:

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

foldr1 f [x]    = [x]
foldr1 f (x:xs) = f x (foldr1 f xs)

Видите, как в случае foldr1 аргумент [x] предшествует аргументу (x:xs)? Для бесконечного списка ввода регистр [x] никогда не встречается, но Haskell все равно проверяет регистр [x] перед обработкой регистра (x:xs). Это делает foldr1 немного менее ленивым, чем foldr.

Например:

λ> head $ foldr unionP [] ([1]:undefined)
1
λ> head $ foldr1 unionP ([1]:undefined)
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
  undefined, called at <interactive>:86:27 in interactive:Ghci4

В вашем вызове composites, где unionAll вызывается в бесконечном списке списков:

bigList = [map (p*) [p..] | p <- primes]

из-за способа определения foldr1 Haskell на самом деле не может вернуть первый элемент из unionAll bigList, пока не проверит, что bigList имеет более одного элемента, но для этого необходимо убедиться, что в списке есть второй элемент:

primes = 2 : ([3..] `minus` composites)

и для этого необходимо проверить, что следующий список не пуст:

[3..] `minus` composites

и для этого необходимо убедиться, что composites начинается с числа, большего, чем 3, но для этого требуется оценка:

unionAll bigList

именно с этого начались все вычисления, и вы получили бесконечный цикл.

В любом случае, все, что вам нужно сделать, чтобы это исправить, это заменить:

unionAll = foldr1 unionP

с немного ленивым:

unionAll = foldr unionP []

и это должно работать.

Вот полная рабочая версия вашего кода с исправлениями minus и unionAll, которая генерирует простые числа без застревания:

union :: Ord a => [a] -> [a] -> [a]
union xs@(x:txs) ys@(y:tys)
    | x < y = x:union txs ys
    | x > y = y:union xs tys
    | otherwise = x:union txs tys

minus :: Ord a => [a] -> [a] -> [a]
minus xs@(x:txs) ys@(y:tys)
    | x < y = x : minus txs ys
    | x > y = minus xs tys
    | otherwise = minus txs ys

unionP :: Ord a => [a] -> [a] -> [a]
unionP (x:xs) ys = x:union xs ys -- poi si va con union

unionAll :: [[Integer]] -> [Integer]
unionAll = foldr unionP []

primes :: [Integer]
primes = 2:[3..] `minus` composites
    where composites = unionAll [map (p*) [p..] | p <- primes]

-- Test
firstPrimes = take 15 primes

Другие вопросы по теме