4

我最近开始学习 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))'

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

4

5 回答 5

7

由于您在内部使用 IO,因此choseRandom您需要更改类型签名:

choseRandom :: [a] -> IO a

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

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

lengthis 的类型,所以 is的[a] -> Int类型length listInt它不是 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))
于 2014-04-18T09:32:26.310 回答
7

第一次处理单子对我来说很难

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

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

这看起来是不是好多了?

现在切入正题:randomRIO正如它们的类型所表明的那样,该函数使用一些全局状态(可能是系统计时器)。因此,您只能使用MonadRandomRIO中的结果。IO

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

于 2014-04-18T09:35:02.190 回答
4

这应该有效:

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 函数的问题是那里的类型签名错误。的类型>>=应该是m a -> (a -> m b) -> m b,因此您应该使用 . 将结果包装回 monad return

所以它的操作是这样的:

randomInt (0, length list)将为您提供IO Int并使用>>=您将从中提取的功能Int!!现在您可以使用函数从列表中提取相应的元素。但是由于输出类型应该是m b你应该使用return.

于 2014-04-18T09:34:08.297 回答
3

首先,您需要修复类型签名 fo choseRandom。其次,我认为如果您使用do符号,您会发现这更容易。

choseRandom :: [a] -> IO a
choseRandom list = do
  let l = length list
  num <- randomInt (0, l-1)
  return (list !! num)
于 2014-04-18T09:35:22.430 回答
0
choseRandom :: [a] -> IO a
choseRandom xs = (xs !!) <$> randomRIO (0, length xs - 1)
于 2018-11-12T16:34:18.773 回答