来自数据类型的Haskell随机数

时间:2010-09-22 12:29:52

标签: haskell random

我对Haskell很新。 我有一个数据类型:

data Sentence= Prop Int
          | No Sentence
          | And [Sentence]
          | Or [Sentence]
          deriving Eq

我已经为它编写了一个Show实例

但是,无论是否有意义,我希望能够生成随机句子。 我怎样才能在Haskell中实现这个目标?

4 个答案:

答案 0 :(得分:6)

随机数生成是“不纯”操作的典型示例,因为调用两次随机生成器当然会产生不同的结果--Haskell的性质不允许这样做。

因此,您需要使用代表随机生成器的所谓monad Gen a,在运行时,会生成类型a 的值。

幸运的是,你可以用一种非常好的语法组合这些生成器......

所以,你只需要一些实现这样一个生成器的library - 我们就这么做了。

randomNo   = No <$> randomSentence
randomProp = Prop <$> choose (1, 10) 
[...]

randomSentence = oneOf [randomNo, randomProp, ...]

答案 1 :(得分:5)

我最喜欢的方法是使用MonadRandom包。虽然它归结为与传递一些RandomGen相同的东西,它会为你做,并确保你不会在这个过程中陷入困境(例如传递一个已经使用过的生成器)。此外,Rand StdGen a对“a s的概率分布”有很好的解释。

对于您的示例,它可能如下所示:

-- a type for probability distributions
type Dist = Rand StdGen

-- pick uniformly between a list of values
uniform :: [a] -> Dist a
uniform xs = do
    ix <- getRandomR (0, length xs - 1)
    return (xs !! ix)

-- return a list of elements generated by the given distribution
randList :: Int -> Dist a -> Dist [a]
randList maxElems dist = do
    elems <- getRandomR (0, maxElems)
    sequence (replicate elems dist)

-- return a probability distribution of sentences
randSentence :: Dist Sentence
randSentence = do
    -- choose one of these four distributions by a uniform distribution
    -- (uniform [...] returns a distribution of distributions)
    dist <- uniform [
        Prop <$> getRandom,
        No <$> randSentence,
        And <$> randList 5 randSentence,
        Or <$> randList 5 randSentence ]
    -- and sample the one we chose
    dist

注意那里的幻数5。这样我们就不会获得20亿个元素列表。您可能想要调整随机生成的列表中术语数量的分布。

要运行它,您可以使用evalRandIO或许多其他内容,例如:

main = print =<< evalRandIO randSentence

答案 2 :(得分:1)

最简单的方法是使用模块System.Random,它位于随机包中,因此您可能必须先安装它。

此模块定义了类型类:

class RandomGen g where
  next :: g -> (Int,g)
  -- ...

class Random r where
  random ::  RandomGen g => g -> (a,g)
  randomR :: RandomGen g => (r,r) -> g -> (a, g)

你需要实现的类型类是Random,具体是第一个函数(因为第二个没有意义,你可以像randomR = const random那样实现。random是什么?你得到一个随机生成器输入,你必须生成你需要的东西,然后重新给它。

要生成随机值,您可以使用State monad,或类似的东西:

random g = (myResult,gn) where
  (random1,g1) = next g
  (random2,g2) = next g2
  -- ...

然后您可以通过此函数使用系统随机生成器:

randomIO :: Random r => IO r

它是预定义的,每次调用都会产生不同的值。

但是,最后你必须自己决定如何定义Random实例。

答案 3 :(得分:1)

pick :: [a] -> IO a
pick xs = randomRIO (0, length xs - 1) >>= return . (xs !!)

选择任何列表。