在Haskell中如何生成指定范围内的随机双精度浮点数列表?

7
我该如何生成一个指定范围内的随机'双精度浮点数'列表?对于像我这样的新手来说,关于此问题的信息有些令人困惑。尝试以下代码:
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个回答

8
你已经接近成功了。你有两个问题。主要问题在于你的类型签名中的[IO Double]部分;这表示你将返回一个IO操作列表,每个操作都可以产生一个double。相反,你想返回一个IO [Double]——一个IO操作,当运行时会产生无限的double列表。如果你只是改变了它,你就快完成了;剩下的问题是你把ab作为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: 如果这不是非常清楚,不要担心;您可以在不完全理解的情况下使用这些内容,并最终理解它们。

随机列表 a b = newStdGen >>= randomRs (a, b) - alternative
1
@monadic 不,那不符合类型检查。 - dave4420
@dave4420 如果写成 randomlist a b = newStdGen >>= return . randomRs (a, b) 就可以进行类型检查。 - fuz
@dave4420 加上一个 return。你也可以写 - 相当无意义的 - randomlist = (((<$> newStdGen) . randomRs) .) . (,) - fuz
1
@FUZxxl抱歉,不知道randomRs的类型。虽然它正在应用中使用,但我本应该注意到... - alternative

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 表达式的类型是 IO something 时,而您的类型签名说该类型应为 [something]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接