Haskell中随机数生成器的函数声明

时间:2014-01-05 06:53:27

标签: haskell random

我刚刚开始学习Haskell,使用“了解大好的Haskell”这本书并完成阅读第9章“输入和输出”。但是当我尝试一个例子时,我遇到了一些奇怪的问题,可以粗略地描述为三个步骤。

1)本书中的示例代码是

finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)  
finiteRandoms 0 gen = ([], gen)  
finiteRandoms n gen =   
    let (value, newGen) = random gen  
        (restOfList, finalGen) = finiteRandoms (n-1) newGen  
    in  (value:restOfList, finalGen)  

但这不能编译。错误消息是

/Users/learn_haskell/randomness.hs:12:15:
    Could not deduce (Eq n) arising from the literal `0'
    from the context (RandomGen g, Random a, Num n)
      bound by the type signature for
                 finiteRandoms :: (RandomGen g, Random a, Num n) =>
                                  n -> g -> ([a], g)
      at /Users/learn_haskell/randomness.hs:10:18-69
    Possible fix:
      add (Eq n) to the context of
        the type signature for
          finiteRandoms :: (RandomGen g, Random a, Num n) =>
                           n -> g -> ([a], g)
    In the pattern: 0
    In an equation for `finiteRandoms': finiteRandoms 0 g = ([], g)
Failed, modules loaded: none.

2)我根据错误消息更改了代码:

finiteRandoms :: (RandomGen g, Random a, Eq n, Num n) => n -> g -> ([a], g)  
finiteRandoms 0 gen = ([], gen)  
finiteRandoms n gen =   
    let (value, newGen) = random gen  
        (restOfList, finalGen) = finiteRandoms (n-1) newGen  
    in  (value:restOfList, finalGen) 

编译很好,但是如果我运行这个函数会出错

*Main> finiteRandoms 5 (mkStdGen 10)

<interactive>:18:1:
    No instance for (Random a0) arising from a use of `finiteRandoms'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Random Bool -- Defined in `System.Random'
      instance Random Foreign.C.Types.CChar -- Defined in `System.Random'
      instance Random Foreign.C.Types.CDouble
        -- Defined in `System.Random'
      ...plus 33 others
    In the expression: finiteRandoms 5 (mkStdGen 10)
    In an equation for `it': it = finiteRandoms 5 (mkStdGen 10)

3)最后,我根据前面的例子更改了代码。

finiteRandoms :: Int -> StdGen -> ([Int], StdGen)  
finiteRandoms 0 gen = ([], gen)  
finiteRandoms n gen =   
    let (value, newGen) = random gen  
        (restOfList, finalGen) = finiteRandoms (n-1) newGen  
    in  (value:restOfList, finalGen)

此时一切似乎都很好。

基本上,我知道为什么第三段代码是正确的,但我不太清楚前两步中出了什么问题。这些错误信息的真正含义是什么?顺便说一下,我在Emacs中使用Haskell模式,我不确定这是否相关。

1 个答案:

答案 0 :(得分:4)

第一个示例不起作用,因为您将变量n与0进行模式匹配。因此您必须将约束Eq正确地放置到typecheck。 Eq类型类定义函数==,如果要检查两个变量是否相等,则需要它。

在第二个示例中,编译器无法确定所需的确切输出类型。但是如果你明确地给它,它将正常工作:

ghci> finiteRandoms 4 (mkStdGen 10) :: ([Int], StdGen)
([-2776415066813205131,-8883108635655729860,-2410613080667970943,-2829092335322664428],587898465 1924298326)

因为第二个示例中的类型签名是多态的,所以您必须指定要获得结果的确切类型。

您还可以获取其他类型的随机数,以防您明确指定:

ghci> finiteRandoms 4 (mkStdGen 10) :: ([Double], StdGen)
([0.7560066907787318,0.7768262448447909,0.3681854379838826,0.9076550534223906],587898465 1924298326)

第三个示例没有任何问题,因为在类型签名本身中,您已明确提到了所需的确切输入和输出类型。此外,不应该注意到,第三个例子不是多态的。