Haskell中定义范围内的随机双精度列表?

时间:2011-07-14 20:04:26

标签: haskell random

如何列出符合定义范围的“Double”类型的随机数?关于像我这样的新手的这个问题的信息有点令人困惑。尝试类似的事情:

randomlist :: Int -> Int -> [IO Double]
randomlist a b = do
  g <- newStdGen
  return (randomRs (a,b) g)

失败,错误:

Couldn't match expected type `[t0]' with actual type `IO StdGen'

你能指出我代码中的错误吗?

2 个答案:

答案 0 :(得分:7)

主要错误在于你的签名。删除它并询问ghci推断类型是什么给出了这个:

*Main> :t randomlist
randomlist :: Random a => a -> a -> IO [a]

当然,如果您愿意,可以将此约束为类型Double -> Double -> IO [Double],如果要进一步限制为整数边界,可以向fromIntegral添加一些调用:

randomlist :: Int -> Int -> IO [Double]
randomlist a b = do
    g <- newStdGen
    return (randomRs (fromIntegral a, fromIntegral b) g)

请注意[IO Double]IO [Double]类型之间的区别。前者是返回Double的计算列表,而后者是返回Double列表的单个计算,这是您在这种情况下所需的。

错误消息可能有点神秘,但它基本上告诉您,因为newStdGen的类型为IO StdGen,所以只有在{{<-类型时才允许绑定do 1}} - 表达式为IO something,而您的类型签名表示类型应为[something]

答案 1 :(得分:7)

你几乎拥有它。你有两个问题。主要问题是您的类型签名的[IO Double]部分;这表示你将返回一个IO动作列表,每个动作都可以生成一个双重动作。相反,你想要返回一个IO [Double] - 一个IO动作,当它运行时,会产生一个无限的双精度列表。如果你改变它,你差不多完成了;剩下的问题是,您abInt,但返回Double。如果你想返回双打,你的界限需要加倍,对于整数也是如此。 (要将Int转换为Double,您可以使用fromIntegral;换句话说,您可以使用round。)因此,为了让您的代码正常工作,您需要更改的是类型签名:

randomlist :: Double -> Double -> IO [Double]
randomlist a b = do
  g <- newStdGen
  return (randomRs (a,b) g)

事实上,如果你没有留下类型签名,一切都会好的; GHC会推断出更通用的类型签名Random a => a -> a -> IO [a]。换句话说,您的函数适用于您可以生成随机成员的任何数据类型。

您还可以略微简化代码。例如,以下内容是等效的:

randomlist :: Random a => a -> a -> IO [a]
randomlist a b = fmap (randomRs (a,b)) newStdGen

fmap :: Functor f => (a -> b) -> f a -> f b函数允许您在函数中应用普通函数。什么是算子?粗略地说,它是某种容器;类型函数,例如[](r ->)IO都是示例。 1 这正是您想要的; randomRs (a,b)的类型为(Random a, RandomGen g) => g -> [a],您需要提供IO StdGen类型的内容,然后返回Random a => IO [a]

还有一种方法可以让你更好(这就是我写它的方式)。如果您导入Control.Applicative,则最终会使用

import Control.Applicative
randomlist :: Random a => a -> a -> IO [a]
randomlist a b = randomRs (a,b) <$> newStdGen

<$>fmap的同义词;它看起来像$,普通的应用程序,因为它们几乎相同。 <$>只是让你进入一个仿函数(这里,IO)。


1:如果不是很清楚,请不要担心;你可以在不完全了解它的情况下使用这些东西,这最终会导致理解。