4

我正在尝试进行植绒模拟,以便更好地自学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 }

我在这里很迷茫,至少可以说我对整个命令式代码在功能语言(和单子)的理解是不稳定的。非常感激任何的帮助!

4

3 回答 3

8

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

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样式,这非常有用。

于 2012-04-08T22:29:36.950 回答
6

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

于 2012-04-08T17:51:04.917 回答
4

类型检查器读取return x : xs(return x) : xs. 如果您编写return (x:xs)它将进行类型检查。

于 2012-04-08T17:46:28.887 回答