【问题标题】:How to make a random list using IO in Haskell如何在 Haskell 中使用 IO 制作随机列表
【发布时间】:2012-04-21 07:57:06
【问题描述】:

我正在尝试进行集群模拟,以便更好地自学haskell。在尝试为需要随机性的模拟生成初始状态时,我遇到了麻烦。我正在尝试生成所有具有随机初始位置和方向的 Boid 列表。

在主函数中我使用

let numBoids = 10
rBoids <- randomBoids numBoids

rBoids 我打算将其存储在 IORef 中,然后我可以更新每一帧,我认为这是正确的做法吗?

这是失败的代码:

-- Type for the flocking algorithm
data Boid = Boid {
    boidPosition  :: Vector2(GLfloat)
  , boidDirection :: Vector2(GLfloat)
  } deriving Show

randomBoids :: Int -> IO ([Boid])
randomBoids 0 = do
  return []
randomBoids n = do
  b <- randomBoid 
  bs <- (randomBoids (n-1))
  return b : bs

randomBoid = do
  pos <- randomVector
  vel <- randomVector
  return (Boid pos vel)

randomVector = do
  x <- randomRIO(-1.0, 1.0)
  y <- randomRIO(-1.0, 1.0)
  return (Vector2 x y)

实际上失败的是return b : bs。如果我将其更改为 return [b] 它会编译。给出的错误是:

Couldn't match expected type `IO [Boid]' with actual type `[a0]'
In the expression: return b : bs
In the expression:
  do { b <- randomBoid;
       bs <- (randomBoids (n - 1));
         return b : bs }
In an equation for `randomBoids':
    randomBoids n
      = do { b <- randomBoid;
             bs <- (randomBoids (n - 1));
               return b : bs }

我在这里很迷茫,至少可以说我对整个函数式命令式代码(和 monad)的理解是不稳定的。任何帮助将不胜感激!

【问题讨论】:

  • "return (b:bs)" 而不是 "return b : bs"

标签: haskell io monads


【解决方案1】:

类型检查器将return x : xs 读取为(return x) : xs。如果你写return (x:xs),它会进行类型检查。

【讨论】:

    【解决方案2】:

    Gangadahr 是正确的。我只想提一下,您可以大大缩短代码:

    import Control.Applicative
    import Control.Monad
    
    randomBoids n = replicateM n randomBoid
    
    randomBoid = Boid <$> randomVector <*> randomVector
    
    randomVector = Vector2 <$> randomRIO (-1, 1) <*> randomRIO (-1, 1)
    

    第一个函数利用了replicateM,当你想重复一个单子动作并收集结果时,这是一个非常有用的函数。后两个函数使用Applicative风格,非常有用。

    【讨论】:

    • 我相信replicateAreplicateM 一样好用,完全适用。
    • @DanBurton replicateA 是 Data.Sequence 中的一个函数,它返回一个在 Seq 上参数化的函子。
    • @is7s 和@DanBurton,Dan 原则上是正确的,您可以定义一个有效的方法。这个定义很好用:replicateA n m = case n of 0 -&gt; pure []; n' -&gt; (:) &lt;$&gt; m &lt;*&gt; replicateA (n - 1) m
    • @GabrielGonzalez 是的,这正是我的想法,感谢您的详细说明。请注意,Data.Sequence 实现的行为有点不同,使用 pure&lt;*&gt; 的 log(n) 调用构造 Seq,而这个公式更符合您的期望:使用 &lt;*&gt; 和 1 的 n 调用pure。当然,在这两种情况下,该操作都会调用n 次。
    【解决方案3】:

    您收到错误的原因是因为return b : bs 会使编译器将其解释为(return b): bs 要解决此问题,您可以将语句更改为return (b:bs)。这将使语句返回IO[Boid]

    【讨论】:

    • 谢谢!我花了至少一个小时尝试各种奇怪的东西来完成这项工作,我觉得很傻。我和 Haskell 正在成为朋友,但 Haskell 先生似乎一直在与我作斗争。我认为 return 很特别,当然它是另一个函数,并且适用相同的优先级规则......
    • 不客气。在 LYAH 上查看此页面 - learnyouahaskell.com/input-and-output。 return 部分解释了为什么在 Haskell 中 return 不同。
    • 您能否在 hpaste 上发布您的代码 sn-p 并在此处链接。我因缺少 Random GFloat 的实例声明而收到编译错误(对于 randomRIO(-1.0,1.0)。GHC 版本 7.0.4
    • 哦,如果您在我的代码中发现任何愚蠢的错误,请随时告诉我!我仍在学习,任何帮助表示赞赏。该代码现在也在 github 上github.com/Dervall/HetaBalls
    猜你喜欢
    • 2019-12-16
    • 1970-01-01
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 2015-08-24
    • 1970-01-01
    • 2014-03-06
    • 1970-01-01
    相关资源
    最近更新 更多