如何列出符合定义范围的“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'
你能指出我代码中的错误吗?
答案 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动作,当它运行时,会产生一个无限的双精度列表。如果你改变它,你差不多完成了;剩下的问题是,您a
和b
为Int
,但返回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:如果不是很清楚,请不要担心;你可以在不完全了解它的情况下使用这些东西,这最终会导致理解。