【问题标题】:Haskell IO Int and IntHaskell IO Int 和 Int
【发布时间】:2014-06-02 18:56:15
【问题描述】:

我最近开始学习 Haskell。我正在尝试编写一个选择随机数组元素的程序:

import System.Random

randomInt :: (Int, Int) -> IO Int
randomInt range = randomRIO range :: IO Int

choseRandom :: [a] -> a
choseRandom list = 
    length list
    >>=
        (\l -> randomInt(0,l-1))
        >>=
            (\num -> (list !! num))

main :: IO ()
main = undefined

我收到以下错误:

Build FAILED

C:\Users\User\Haskell\Real\src\Main.hs: line 7, column 9:
  Couldn't match expected type `IO Int' with actual type `Int'

    In the return type of a call of `length'

    In the first argument of `(>>=)', namely `length list'

    In the first argument of `(>>=)', namely

      `length list >>= (\ l -> randomInt (0, l - 1))'

我做错了什么?第一次处理 monad 对我来说很难

【问题讨论】:

    标签: haskell types monads io-monad


    【解决方案1】:

    由于您在 choseRandom 中使用 IO,您需要更改类型签名:

    choseRandom :: [a] -> IO a
    

    其次,您不需要使用>>= 来获取列表的长度。 >>= 有类型

    Monad m => m a -> (a -> m b) -> m b
    

    length 的类型是[a] -> Int,所以length list 的类型是Int,这不是monad。

    调用randomInt时可以直接计算:

    choseRandom :: [a] -> IO a
    choseRandom list = 
        randomInt(0, length list) >>= (\num -> return (list !! num))
    

    相同
    choseRandom :: [a] -> IO a
    choseRandom list = fmap (\num -> (list !! num)) (randomInt(0, length list))
    

    【讨论】:

    • 丑陋的(\num -> (list !! num))一旦有了fmap的想法,就可以缩短为(list !!)
    【解决方案2】:

    这应该可行:

    import System.Random
    
    randomInt :: (Int, Int) -> IO Int
    randomInt range = randomRIO range :: IO Int
    
    choseRandom :: [b] -> IO b
    choseRandom list = randomInt (0, length list) >>= \x -> return $ list !! x
    

    我觉得这更惯用:

    choseRandom list = do
     a <- randomInt (0, length list)
     return $ list !! a
    

    您的 selectedRandom 函数的问题是类型签名在那里错误。 &gt;&gt;= 的类型应该是 m a -&gt; (a -&gt; m b) -&gt; m b,因此您应该使用 return 将结果包装回 monad。

    所以它的操作是这样的:

    randomInt (0, length list) 会给你IO Int 并且使用&gt;&gt;= 函数你会从中提取出Int。现在您可以使用!! 函数从列表中提取相应的元素。但是由于输出类型应该是m b,你应该使用return将它包装回monad。

    【讨论】:

      【解决方案3】:

      第一次处理 monad 对我来说很难

      是的,而且您通过避免语法支持使事情变得更加困难。就这样写吧:

      choseRandom list = do
         let l = length list
         num <- randomInt(0,l-1)
         return (list !! num)
      

      这样看起来是不是好多了?

      现在重点:randomRIO 函数,正如它们的类型所示,使用一些全局状态(可能是系统计时器)。因此,您只能在 IO Monad 中使用来自 RandomRIO 的结果。

      另一种方法是在main 函数中初始化一个随机生成器,并将这个生成器传递给需要“随机”值的纯函数。

      【讨论】:

      • 这不是系统计时器,而是一个共享可变变量,其内容类型为StdGen,与库中的纯函数显式传递的生成器相同。基于 IO 的随机数函数本质上只是将对该变量的读取和写入包装在纯函数周围。
      【解决方案4】:

      首先你需要修复choseRandom的类型签名。其次,我认为如果您使用do 表示法,您会发现这更容易。

      choseRandom :: [a] -> IO a
      choseRandom list = do
        let l = length list
        num <- randomInt (0, l-1)
        return (list !! num)
      

      【讨论】:

      • 对于正在学习 monads 的人来说,我一开始会避免使用 do 表示法
      【解决方案5】:
      choseRandom :: [a] -> IO a
      choseRandom xs = (xs !!) <$> randomRIO (0, length xs - 1)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-16
        • 2012-06-13
        • 2011-05-13
        相关资源
        最近更新 更多