簡體   English   中英

Haskell模式匹配不同的數據類型

[英]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將可以像這樣來進行模式匹配。

有很多不同的方法可以解決此問題,根據您游戲的其他方面,一個簡單的解決方案是將BulletAsteroid合並為單個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM