2016-10-05 4 views
1

EDITED!Создать бесконечный список в Haskell с IO

Я пытаюсь написать алгоритм, который генерирует (бесконечный) список простых чисел Мерсена. У меня есть алгоритм (Rabin-Miller), который возвращает IO Bool, в зависимости от того, считает ли алгоритм число простым или нет. Этот алгоритм называется rabinMiller, и он принимает два аргумента: первый - Integer, который говорит что-то о безопасности списка, и второе число, которое мы отправляем на тест primality.

Я хочу индексировать список всех простых чисел (называемых простых чисел), и для каждого p в простых количествах, проверьте, считает ли rabinMiller, что 2^p - 1 является простым. Если это так, мы представляем 2^p - 1 в список.

То, что я сейчас это:

rbMersenneGen :: Integer -> IO(IO [Integer]) 
rbMersenneGen k = do return $ rbMersenneGens 0 k 

rbMersenneGens :: Int -> Integer -> IO [Integer] 
rbMersenneGens x k = 
    let n = primes!!x 
     m = (2^n) - 1 
    in do 
    prim <- rabinMiller k m 
    if prim then 
     m:rbMersenneGens (x + 1) k 
    else 
     rbMersenneGens (x + 1) k 

Но это на самом деле не работает, так как я не могу получить тип m:rbMersenneGens (x + 1) k правой. Я получаю ошибку, что m является Integer вместо [Integer], а затем типа всей m:rbMersenneGens (x + 1) k неправильно и создает другие ошибки

+0

Вы должны: 'if prim, а затем xs <- rbMersenneGens (x + 1) k; return m: xs' для соответствия типам. – Bakuriu

+0

Проблема в том, что Рабин-Миллер использует случайные числа. У Haskell есть проблема со случайными числами, потому что они по сути нечистые. Если это ваша реализация Рабина-Миллера, вам нужно переписать его, чтобы принять случайное семя в качестве аргумента. Или используйте другой тест на соответствие. –

ответ

1

Ваши оригинальные типы в порядке; нам просто нужно немного изменить определение rbMersenneGens. Я собираюсь реорганизовать функцию немного.

rbMersenneGen :: Integer -> IO [Integer] 
rbMersenneGen k = rbMersenneGens 0 k 

rbMersenneGens :: Int -> Integer -> IO [Integer] 
rbMersenneGens x k = 
    let n = primes!!x 
     m = (2^n) - 1 
    in do 
     prim <- rabinMiller k m 
     -- We call rmMersenneGens the same way in either case; 
     -- the only difference is which function we apply to the result 
     (if prim then (m:) else id) (rbMersenneGens (x+1) k) 

Ваша проблема заключается в том, что prim должна быть Bool для выражения if-else, и выражение do необходимо оценить до IO [Integer] значения. Это означает, что rabinMiller нужно будет вернуть значение [Bool] вместо IO Bool, что и есть. Исправление состоит в том, чтобы заменить if еще на Data.Bool.bool :: a -> a -> Bool -> a.

Это не тип проверки ...

rbMersenneGens :: Int -> Integer -> IO [Integer] 
rbMersenneGens x k = 
    let n = primes!!x 
     m = (2^n) - 1 
     prim = rabinMiller k m -- IO Bool 
    in (bool id (m:) prim) (rbMersenneGens (x+1) k) 

, потому что мы все еще пытаются лечить prim как Bool, а не IO Bool, но это делает:

rbMersenneGens :: Int -> Integer -> IO [Integer] 
rbMersenneGens x k = 
    let n = primes!!x 
     m = (2^n) - 1 
     prim = rabinMiller k m 
    in (bool id (m:)) <$> prim <*> (rbMersenneGens (x+1) k) 

Мы можем» т сделать if работы с IO Bool, но мы может лифт bool для работы с одним. На самом деле нам нужно только обрабатывать IO как Applicative, а не Monad. Мы заменяем >>= на fmap (в его инфиксной форме <$>).

+0

Не будет ли этот цикл навсегда (IO является строгим аппликативным)? – chi

+0

Большое вам спасибо! Это сработало! Только «(m:)» и «id» необходимо переключать (в последней строке), так как 'bool' ведет себя странно. И если я запустил 'rbMersenneGen k', он никогда не вернет что-то ... Но я могу решить это, сделав' rbMersenneGens n _ = return [] 'для некоторого n – Mal

+0

@chi да это действительно работает навсегда, см. Мой другой комментарий ... – Mal

0

У вас есть блок сделать в rbMersenneGens. Результат блока do всегда находится в той же монаде, что и внутри. Поскольку вы звоните rabinMiller внутри, который находится в монаде IO, результат блока do находится в монаде IO. Однако вы заявили, что rbMersenneGens возвращает [Integer], который не соответствует выведенному типу.

+0

Да. Я думаю, что OP может попасть в эту общую ловушку (http://stackoverflow.com/documentation/haskell/1904/io/7236/getting-the-a-out-of-io-a#t=201610051636053171053) –

+0

Но если я позволю ему вернуть '[IO Integer]' и 'rbMersenneGen'' IO [IO Integer] 'то же происходит ошибка (и еще две) – Mal

+0

Объявленное возвращаемое значение означает, что выражение' do' работает в монаде списка, и 'prim' должен быть' Bool', поскольку он используется с выражением 'if'. Это означает, что 'rabinMiller' нужно вернуть значение' [Bool] ', но оно возвращает' IO Bool'. – chepner

Смежные вопросы