我正在创建一个Asteroids克隆,并希望创建一个移动功能。我以为可以在数据类型上使用模式匹配,但是类型签名当然不符合实际方法。 如果t
函数中Moving
数据类型中的move
参数是Bullet
数据类型,我想使用不同的代码,但是尝试了。除了制作专门的移动功能外,还有其他想法(在这里可能会更好,但是我仍然想知道是否还有其他方法)。
因此,我有Moving Asteroid
和Moving Bullet
,并且想对Asteroid
或Bullet
的类型(或其他我未在此处发布的类型)进行模式匹配举个最小的例子)
move
函数应该用一句话来做:使用环绕式移动Moving o
以外的所有类型的Moving Bullet
。
一些上下文代码:
data Moving s = Moving {
position :: Position,
velocity :: Velocity,
size :: Float,
specifics :: s
}
data Bullet = Bullet {
damage :: Int
}
| DeadBullet
data Asteroid = Asteroid
| DeadAsteroid
move :: Float -> Moving o -> Moving o
move secs (Moving (x, y) v@(vx, vy) s t@(Bullet _)) = Moving (x', y') v s t
where x' = (x + vx * secs)
y' = (y + vy * secs)
move secs (Moving (x, y) v@(vx, vy) s t) = Moving (x', y') v s t
where x' = (x + vx * secs) `mod'` width
y' = (y + vy * secs) `mod'` height
错误:
src\Controller.hs:100:42: error:
* Couldn't match expected type `o' with actual type `Bullet'
答案 0 :(得分:3)
您无法通过模式匹配,因为Moving o
是多态的。如果您有一个仅移动项目符号的功能,则Moving Bullet
可以像这样来进行模式匹配。
有许多不同的方法可以解决此问题。根据您游戏的其他方面,一个简单的解决方案是将Bullet
和Asteroid
整合到单个Movable
数据中可以用来进行模式匹配的类型:
data Moving = Moving {
position :: Position,
velocity :: Velocity,
size :: Float,
specifics :: Movable
}
data Movable = B Bullet | A Asteroid
data Bullet = Bullet {
damage :: Int
}
| DeadBullet
data Asteroid = Asteroid
| DeadAsteroid
move :: Float -> Moving -> Moving
move secs (Moving (x, y) v@(vx, vy) s t@(B _)) = Moving (x', y') v s t
where x' = (x + vx * secs)
y' = (y + vy * secs)
move secs (Moving (x, y) v@(vx, vy) s t) = Moving (x', y') v s t
where x' = (x + vx * secs) `mod'` width
y' = (y + vy * secs) `mod'` height
答案 1 :(得分:2)
我完全不同意jkeuhlen(或chepner in your first question on this):您可能根本不需要类型区分,可以将其全部保留在值级别。
但是您也可以在类型级别上执行此操作,这确实有些道理,因为移动对象绝不能更改其类型。现在,您可以在其中使用课程。您可以将move
用作方法:
type Time = Float
class ObjSpecific s where
move :: Time -> Moving s -> Moving s
instance ObjSpecific Bullet where
move δt (Moving p v s t) = -- definition without edge-wrapping
instance ObjSpecific Asteroid where
move δt (...) = ... -- definition with edge-wrapping
顺便说一句,我想您应该在子弹离开屏幕后采取一些措施来摆脱子弹...也许把它变成move :: Time -> Moving s -> Maybe (Moving s)
。
答案 2 :(得分:2)
除了jkeuhlen的答案之外,您还可以使用类型类:
class Moveable a where
move :: Float -> a -> a
position :: a -> Position
velocity :: a -> Velocity
data Asteroid = Asteroid {
asteroidP :: Position,
asteroidV :: Velocity
}
instance Moveable Asteroid where
move secs (Asteroid (x, y) v@(vx, vy)) =
Asteroid ((x + secs*vx) `mod'` width, (y + secs*vy) `mod'` height) v
position = asteroidP
velocity = asteroidV
Bullet
也是如此。
这看起来类似于您可能熟悉的OO继承方法。但是请记住,此代码中的Moveable
是一组类型,但本身不是类型。您不能创建可移动物体的列表,也不能将小行星和子弹都放入其中。子弹和小行星仍然是不同的类型。如果要将它们都放在列表中,则必须使用jkeulen的方法(将两者组合在一起当然没有什么问题。)