你已经接近成功了。你有两个问题。主要问题在于你的类型签名中的
[IO Double]
部分;这表示你将返回一个IO操作列表,每个操作都可以产生一个double。相反,你想返回一个
IO [Double]
——一个IO操作,当运行时会产生无限的double列表。如果你只是改变了它,你就快完成了;剩下的问题是你把
a
和
b
作为
Int
,但返回
Double
。如果你想返回doubles,你的范围需要是doubles,整数也是如此。(要将
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
函数允许您在functor内部应用普通函数。什么是functor?粗略地说,它是某种容器;例如
[]
、
(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: 如果这不是非常清楚,不要担心;您可以在不完全理解的情况下使用这些内容,并最终理解它们。
randomlist a b = newStdGen >>= return . randomRs (a, b)
就可以进行类型检查。 - fuzreturn
。你也可以写 - 相当无意义的 -randomlist = (((<$> newStdGen) . randomRs) .) . (,)
。 - fuz