[英]Haskell Pattern matching on different data types
我正在創建一個Asteroids克隆並想要創建一個移動功能。 我以為可以在數據類型上使用模式匹配,但是類型簽名當然不符合實際方法。 如果在move
函數的Moving
data類型中的t
參數是Bullet
數據類型,並嘗試了此操作,則我想使用其他代碼,但這不起作用。 除了制作專門的移動功能(在這里可能會更好,但我仍然想知道是否還有其他方法)之外,還有其他想法。
因此,我有“ Moving Asteroid
和“ Moving Bullet
並希望根據“ Asteroid
或“ Bullet
類型(或其他我未在此處發布以提供最小示例的類型)進行模式匹配
move
函數應該用一句話來做:使用環繞式Moving o
除“ Moving Bullet
之外的所有類型的“ Moving o
。
一些上下文代碼:
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'
您無法通過這種方式進行模式匹配,因為“ Moving o
是多態的。 如果您具有僅Moving Bullet
的功能,則“ 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
我完全不同意jkeuhlen(或您在第一個問題上的chepner ):您可能根本不需要類型區分,可以將其全部保留在價值水平上。
但你也可以做到這一點的類型水平,這確實讓一些有意義的,因為移動的對象不應該改變它的類型。 現在,您可以在其中使用課程。 您可以只使用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)
。
除了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的方法(將兩者組合在一起當然沒有錯)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.