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

Chr*_*s.Q 3 random haskell

我刚开始使用“Learn You a Haskell for Great Good”这本书开始学习 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)  
Run Code Online (Sandbox Code Playgroud)

但这无法编译。错误信息是

/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.
Run Code Online (Sandbox Code Playgroud)

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) 
Run Code Online (Sandbox Code Playgroud)

编译没问题,但是运行这个函数会报错

*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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

此时一切似乎都很好。

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

Sib*_*ibi 5

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

在第二个示例中,编译器无法确定您想要的确切输出类型。但如果你明确给出它,它会正常工作:

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

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

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

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

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