简体   繁体   中英

Need help figuring out a function in Haskell

So I have a function fight,which takes two objects in the following manner:

fight :: (Combatant a, Combatant b) => (a, b) -> (a, b)
fight (fighter1, fighter2) =
let damageTo1 = ((attack fighter2) - (defense fighter1))
  damageTo2 = ((attack fighter1) - (defense fighter2))
 in (damage fighter1 damageTo1, damage fighter2 damageTo2)

The combatant class is as follows:

 class Combatant a where
 health  :: a -> Int
 attack  :: a -> Int
 defense :: a -> Int
 level   :: a -> Int
 damage  :: a -> Int -> a

I have two object types, Player, and Monster. The player has to fight monsters around him,so the monsters are actually in a list like [Monster1,Monster2,Monster3,Monster4...etc] and the player has to fight each single one.. so the function calls would go something like

  fight(player,Monster1)
  fight(player,Monster2)....etc..

However,I am not exactly sure how to make that happen ..I thought a function like map would be useful here but I'm not sure.. Ideas would be appreciated :)

You probably want a fold, a map, or 🌟both🌟. Given:

class Combatant a where
  health  :: a -> Int
  attack  :: a -> Int
  defense :: a -> Int
  damage  :: a -> Int -> a

data Person = Person { personHP :: Int } deriving Show
data Monster = Monster { monsterHP :: Int } deriving Show

-- We will assume for time that your HP determines your attack.
instance Combatant Person where
  health = personHP
  attack = personHP
  defense _ = 0
  damage (Person hp) dmg = Person (hp - dmg)

instance Combatant Monster where
  health = monsterHP
  attack = monsterHP
  defense _ = 0
  damage (Monster hp) dmg = Monster (hp - dmg)

fight :: (Combatant a, Combatant b) => a -> b -> (a, b)
fight fighter1 fighter2 =
  let damageTo1 = attack fighter2 - defense fighter1
      damageTo2 = attack fighter1 - defense fighter2
  in (damage fighter1 damageTo1, damage fighter2 damageTo2)

To do this, it's best to split up fight so that it only returns the first combatant. As fight is symmetrical, we can figure out what happens to the second combatant by reversing the order of our arguments. That looks like this

fight :: (Combatant a, Combatant b) => a -> b -> a
fight fighter1 fighter2 =
  damage fighter1 (attack fighter2 - defense fighter1)

and this

λ> fight (Person 10) (Monster 5)
Person {personHP = 5}
λ> (flip fight) (Person 10) (Monster 5)
Monster {monsterHP = -5}
λ> fight (Monster 5) (Person 10)
Monster {monsterHP = -5}

Given a list of monsters and a single person:

λ> let person = Person 15
λ> let monsters = map Monster [1..4]
λ> monsters
[Monster {monsterHP = 1},Monster {monsterHP = 2},Monster {monsterHP = 3},Monster {monsterHP = 4}]

We're probably interested in asking two questions:

  • What happens to the player after her encounter with all the monsters?
  • What happens to each monster after her encounter with the player?

The second question is dispatched with a map, as @Aky mentioned.

λ> map (`fight` person) monsters
[Monster {monsterHP = -14},Monster {monsterHP = -13},Monster {monsterHP = -12},Monster {monsterHP = -11}]

Ouch. Whereas the answer to the first question might look like this:

λ> fight (fight (fight person (monsters !! 0)) (monsters !! 1)) (monsters !! 2) -- ... ugh, how tiresome ...

But this is what functional programmers call a fold left , which in Haskell goes by the foldl' function (ignore the baroque apostrophe).

λ> foldl' _ person monsters    
<interactive>:137:8:
    Found hole ‘_’ with type: Person -> Monster -> Person
λ> foldl' fight person monsters
Person {personHP = 5}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM