[英]List comprehension with IO Bool in Haskell
我正在尝试通过Miller Rabin素数检查来检查Mersenne数,以找到可能的Mersenne素数。
如果mersennes
是Mersenne数的无限列表,我想做些类似的事情:
probableMersennePrimes :: IO [Integer]
probableMersennePrimes = [ n | n <- mersennes, primeMillerRabin n ]
其中primeMillerRabin :: Integer -> IO Bool
。
作为附带问题,类型应该是IO [Integer]
还是[IO Integer]
?
您可以使用pipes
ListT
流式传输数字的无限列表:
import Control.Monad
import Data.Monoid
import Pipes
mersennes :: [Integer]
mersennes = undefined
primeMillerRabin :: Integer -> IO Bool
primeMillerRabin = undefined
probableMersennePrimes :: ListT IO Integer
probableMersennePrimes = do
n <- Select (each mersennes)
continue <- lift (primeMillerRabin n)
guard continue
return n
main = runListT $ do
n <- probableMersennePrimes
lift (print n)
mempty
即使mersennes
列表是无限的,它也可以工作,并且在您计算结果时将流式传输结果。
如果将primeMillerRabin
更改为RandomGen g => Integer -> State g Bool
,则可以使用filterM
进行此filterM
。
probableMersennePrimes :: RandomGen g => g -> [Integer]
probableMersennePrimes = evalState $ filterM primeMillerRabin mersennes
通过使用evalState
,我们放弃了filterM primeMillerRabin mersennes
计算的最终状态,因此我们对此并不严格。 但这是一件好事,因为最终状态只有在到达进程的mersennes
列表的末尾mersennes
,该列表是无限的,并且没有结束。
这允许计算延迟生成probableMersennePrimes gen
元素。
由于随机数生成器的工作原理,您无法在返回IO
操作时执行此操作。 IO计算需要知道结束状态是什么,以便可以在计算之后生成另一个随机数,因此它必须无限循环,以寻找无限列表的末尾。
但是,不要只是相信我,可以尝试一下:
module SO26307073 where
import Control.Monad.State
import System.Random
-- find how many times a factor divides a number
-- (p^s * d) `factorBy` p == (s,d) iff d `rem` p /= 0
factorBy :: Integral a => a -> a -> (Int,a)
factorBy n p = (length steps - 1, fst $ last steps)
where steps = takeWhile ((==0) . snd) $ iterate (flip quotRem 2 . fst) (n, 0)
mersennes :: Num a => [a]
mersennes = [ 2^n - 1 | n <- [2..] ]
type RandomRM m = (Integer, Integer) -> m Integer
primeMillerRabinWith :: Monad m => RandomRM m -> Integer -> m Bool
primeMillerRabinWith randomRM n = do
let nMinus1 = n-1
(s,d) = nMinus1 `factorBy` 2
liftM (all id) . replicateM 10 $ do
a <- randomRM (2, nMinus1)
let x = (a^d) `mod` n
let xs = take s $ iterate (\x -> (x^2) `mod` n) x
return $ x == 1 || any (== nMinus1) xs
probableMersennePrimesWith :: Monad m => RandomRM m -> m [Integer]
probableMersennePrimesWith randomRM = filterM (primeMillerRabinWith randomRM) mersennes
probableMersennePrimesPure :: RandomGen g => g -> [Integer]
probableMersennePrimesPure = evalState . probableMersennePrimesWith $ state . randomR
probableMersennePrimesIO :: IO [Integer]
probableMersennePrimesIO = probableMersennePrimesWith $ randomRIO
请注意, probableMersennePrimesIO
和probableMersennePrimesPure
仅使用不同的方式绘制随机数。
弹出ghci,我们可以看到纯版本可以正常工作,而IO版本只是挂起:
λ import Control.Applicative
λ import System.Random
λ :l SO26307073
[1 of 1] Compiling SO26307073 ( SO26307073.hs, interpreted )
Ok, modules loaded: SO26307073.
λ take 0 . probableMersennePrimesPure <$> newStdGen
Loading package array-0.5.0.0 ... linking ... done.
Loading package deepseq-1.3.0.2 ... linking ... done.
Loading package old-locale-1.0.0.6 ... linking ... done.
Loading package time-1.4.2 ... linking ... done.
Loading package random-1.0.1.1 ... linking ... done.
Loading package transformers-0.4.1.0 ... linking ... done.
Loading package mtl-2.2.1 ... linking ... done.
[]
λ take 5 . probableMersennePrimesPure <$> newStdGen
[3,7,31,127,8191]
λ take 5 <$> probableMersennePrimesIO
^CInterrupted.
所有其他答案都很好。 但是,我认为解决此问题的最简单方法是惰性IO,值得考虑。 我还认为,在这种特殊情况下,由于不涉及系统资源(文件句柄等),因此惰性IO是无害的。
您只需要为惰性IO重新定义特殊的filterM
。
import System.IO.Unsafe (unsafeInterleaveIO)
filterMIO :: (a -> IO Bool) -> [a] -> IO [a]
filterMIO p = go
where
go [] = return []
go (x:xs) = do
xs' <- unsafeInterleaveIO (go xs)
b <- p x
return $ if b then (x:xs') else xs'
probableMersennePrimes :: IO [Integer]
probableMersennePrimes = filteMIO primeMillerRabin mersennes
请注意,这将对无限列表有效。 这就是为什么我们实际上使用了惰性IO的原因!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.