Haskell:随机硬币实例

时间:2016-10-15 19:12:35

标签: haskell random generator

我已经定义了Coin数据类型:

data Coin = H | T
    deriving (Bounded, Eq, Enum, Ord, Show)

我现在必须编写一个Random Coin实例,给出以下框架:

instance Random Coin where
randomR (l, h) g = undefined
random           = undefined

显然,这个随机实例应该返回H或T.我开始理解Monads如何工作,但是,我对初始随机生成器感到困惑。我得到一个随机生成器返回(a, gen),但我们在哪里得到初始生成器来创建第一个随机硬币?我目前有以下内容:

instance Random Coin where
randomR (l, h) g = case randomR(l, h) g of
                   (c, g') -> (c, g')
random           = getStdRandom(randomR(minBound :: Coin, maxBound :: Coin))

我对random函数感到特别困惑,因为特定的表达式似乎返回了类型IO Coin。非常感谢任何帮助启发我!

3 个答案:

答案 0 :(得分:4)

Random类的要点是它的方法为你提供了一个生成器然后要求你创建下一个值和生成器。每当您的类派生EnumBounded时,实际上都有一个用于创建随机实例的样板方法:

instance Random Coin where
  randomR (lo, hi) g = let (a, g') = randomR (fromEnum lo, fromEnum hi) g in (toEnum a, g')
  random = randomR (minBound, maxBound)

(事实上,already a package可以为你做到这一点。)

答案 1 :(得分:4)

您定义的功能有类型

randomR :: RandomGen g => (Coin, Coin) -> StdGen -> (Coin, StdGen)
random  :: RandomGen g => StdGen                 -> (Coin, StdGen)

换句话说,你已经给出了一个随机生成器 - randomR的第二个参数和随机的第一个参数。任何获取系统随机生成器的尝试都会导致类型错误,因为突然之间您已经在混合中添加了IO,并且在这些类型中无处发生。

通常,随机实例会将一个基于Integer的函数转换为所需的特定类型。例如,我们可以使用

 randomR :: (RandomGen g) => (Integer, Integer) -> g -> (Integer, g)

生成范围(0,1)中的数字,并将0转换为H,将1转换为T

instance Random Coin where
  randomR (low,high) generator =
    case randomR (toInt low, toInt high) generator of
      (i,g) -> (toCoin i, g)
   where
    toInt :: Coin -> Integer
    toInt H = 0
    toInt T = 1
    toCoin 0 = H
    toCoin 1 = T

我将random留给您定义。

顺便说一下,您可能会发现这基本上与Bool必须具有的实例相同,因此解决问题的一种明智方法也是检查source code for the "random" package并搜索& #34;实例Random Bool"。

之后,您可以将它与任何随机生成器一起使用,如下所示:

> let gen = mkStdGen 1 in take 10 $ randomRs (H,T) gen
[T,T,T,H,H,T,H,H,H,T]

答案 2 :(得分:1)

我非常喜欢Alec的实施,我自己做了。通过分别定义randomrandomR函数,它可以更容易地用于多个实例(更少的样板!) - 而且对于我的钱 - 也更容易阅读。

import Control.Arrow (first)

boundedRandom :: (Bounded b, Random b, RandomGen g) => g -> (b, g)
boundedRandom = randomR (minBound, maxBound)

enumRandomR :: (Enum e, RandomGen g) => (e,e) -> g -> (e,g)
enumRandomR (min,max) g = toEnum `first` randomR (fromEnum min, fromEnum max) g

instance Random Coin where
        random = boundedRandom
        randomR = enumRandomR

最好的部分是,对于同时实现EnumBounded的任何类型,这都是即插即用的!

data Suite
        = Spade
        | Heart
        | Club
        | Diamond
  deriving (Bounded, Enum)

instance Random Suite where
        random = boundedRandom
        randomR = enumRandomR