从无限列表中选取2个唯一值

时间:2017-03-23 09:57:57

标签: haskell

我已经定义了一个函数,用于从下限和上限之间的无限随机数列表中获取1个素数:

randomNumbers :: Random a => a -> a -> [a]
randomNumbers bottom top = randomRs (bottom, top) $ unsafePerformIO newStdGen    

-- | Generates a random prime number of k bits length
randPrime :: Integer -> Integer
randPrime k = head $ filter isPrime ( randomNumbers (2^(k-1)) ((2^k)-1) )

但有时我需要两个唯一的素数pq p =! q

这感觉完全错了:

-- | Generates random prime number q until it differs from a given prime number p
randUniquePrime :: Integer -> Integer -> Integer
randUniquePrime p k
    | q == p = randUniquePrime p k
    | otherwise = q
    where q = genPrime k

最有效的方法是什么?功能和方法是什么?

1 个答案:

答案 0 :(得分:2)

首先,您永远不想使用unsafePerformIO。是的,它有效用途,但作为初学者,你永远不想触摸它。因此,我们先修复randomNumbersrandPrime

randomNumbers :: Random a => a -> a -> IO [a]
randomNumbers bottom top = randomRs (bottom, top) <$> newStdGen    

-- | Generates a random prime number of k bits length
randPrime :: Integer -> IO Integer
randPrime k = head . filter isPrime <$> randomNumbers (2^(k-1)) ((2^k)-1)

现在要获得多个素数,我们可以让randPrime返回素数列表并从该列表中过滤重复项:

import Data.List (nub)

-- | Generates random prime numbers of k bits length
randPrimes :: Integer -> IO [Integer]
randPrimes k = filter isPrime <$> randomNumbers (2^(k-1)) ((2^k)-1)

-- | Generates random prime numbers of k bits length
randUniqPrimes :: Integer -> IO [Integer]
randUniqPrimes k = nub <$> randPrimes k

-- Now we can get any number including two unique random primes using take
-- or similar.
randPrimePair :: Integer -> IO (Integer, Integer)
randPrimePair k = (\(x:y:_) -> (x,y)) <$> randUniqPrimes k

如果您在不使用IO的情况下不知道如何正常使用unsafeCoerce,这是一个非常简短的介绍:

  1. 如果您的值为IO a类型且函数为a -> b,则可以使用

    fmap, (<$>) :: (a -> b) -> (IO a -> IO b)
    -- eg.
    nub <$> randPrimes k
    -- or
    fmap nub (randPrimes k)
    

    应用该功能。

  2. 应用一个多个IO值的多个参数的函数。为此,有

    (<*>) :: IO (a -> b) -> (IO a -> IO b)
    -- which you can use like this:
    getThreeLines :: IO (String, String, String)
    getThreeLines = (,,,) <$> getLine <*> getLine <*> getLine
    -- Reminder:
    (,,,) :: a -> b -> c -> (a, b, c)
    
  3. 如果您的值为IO a类型且值为a -> IO b,则可以使用

    (=<<) :: (a -> IO b) -> (IO a -> IO b)
    -- or the commonly used ugly flipped variant
    (>>=) :: IO a -> (a -> IO b) -> IO b
    -- eg.
    print =<< getLine
    
  4. 有时您可能需要将纯值提升到IO,因为pure :: a -> IO apure通常也名为return