我对Haskell很新。 我有一个数据类型:
data Sentence= Prop Int
| No Sentence
| And [Sentence]
| Or [Sentence]
deriving Eq
我已经为它编写了一个Show实例
但是,无论是否有意义,我希望能够生成随机句子。 我怎样才能在Haskell中实现这个目标?
答案 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 !!)
选择任何列表。