生成随机值列表并将其打印到Haskell中的标准输出

时间:2014-10-06 16:16:24

标签: haskell

我对Haskell很陌生,我正在努力实现一些相对简单的事情:生成一个随机数列表并将它们打印到标准输出。

由于随机概念与FP世界中的函数纯度非常相反(即方法应该返回相同输入的相同结果),我理解在这种情况下,Haskell中的System.Random模块返回IO而不是行动。

到目前为止,我的代码如下所示:

import System.Random

randomNumber :: (Random a) => (a, a) -> IO a
randomNumber (a,b) = randomRIO(a,b)

main :: IO ()
main = do
   points <- sequence (map (\n -> randomNumber ((-1.0), 1.0)) [1..10])
   print points

这个想法很简单:生成一个包含十个随机元素的列表(可能有更好的方法来实现它)。我的第一种方法是创建一个函数,该函数返回一个随机数(在本例中为randomNumber,类型为IO a),并在映射元素列表时使用它(生成IO动作列表,{ {1}})。

根据我的理解,IO [a]类型为sequence (map (\n -> randomNumber ((-1.0), 1.0)) [1..10]),但我不知道如何使用它。如何才能真正将点数用作IO [a]类型的值而不是[a]

编辑:在do“block”中添加print函数会产生一些我真不知道如何摆脱的错误。

IO [a]

2 个答案:

答案 0 :(得分:4)

为什么会这样?

您的所有错误中都有一条特定消息:The type variable ‘a0’ is ambiguous。为什么会这样?好吧,randomNumber适用于Random的任何实例,并且有很多实例。 -1.0包含Num,因为您希望能够否定某个值。此外,值1.0本身也断定您的类型必须是Fractional的实例。这减少了在这种情况下可以使用的类型数量,但它仍然不是唯一的:FloatDouble和其他四种适合。

此时,编译器放弃了,需要告诉它你真正想要使用的实例。

如何解决此问题

有很多方法可以解决这个问题。首先,我们可以引入一个小辅助函数:

-- fix a to double
randomDouble :: (Double, Double) -> IO Double
randomDouble = randomNumber

或者我们可以注释模糊的1.0

的类型
points <- sequence (map (\n -> randomNumber ((-1.0), 1.0 :: Double)) [1..10])
--                                                   ^^^ as a Double

或者我们可以注释列表的类型:

print (points :: [Double])
--     ^^^^^^^^^^^^^^^^^^  points is a list of Doubles

您选择哪一个实际上或多或少是风格和个人偏好的问题。话虽如此,sequence . map f $ xs可以写成mapM f xs,但由于您确实拥有IO a,因此replicateM $ randomNumber (...)更适合您。 mapMreplicateM都可以在Control.Monad中找到。

TL; DR

当GHC对你的模糊类型大吼大叫时,请注明它们。

答案 1 :(得分:3)

几点:

  1. 您调用函数randomNumber,但允许它采用属于Random类的任何类型(包括Chars等)。如果您只想让它取数字,您应该更改签名以符合其目的(randomNumber :: (Int,Int) -> IO Int)或更一般,randomNumber :: (Num n. Random n) => (n,n) -> IO n

  2. sequence获取操作列表([IO a]),并返回IO monad(IO [a])中的列表。它基本上只执行每个操作,存储结果,然后在IO中重新包装列表。你可以试试replicateM 10 $ randomNumber (1,10)之类的东西。 replicateM接受一个I​​nt和一个动作来执行,并返回一个已执行动作的列表(正如Zeta指出的那样,sequence在内部用于调用replicateM)。

  3. (由于某些原因,代码块对我不起作用,所以我把所有内容写成“中缀代码”。)