Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:2023:haskell:l09 [2023/04/24 16:29] bogdan.deac |
pp:2023:haskell:l09 [2023/05/02 22:15] (current) bogdan.deac |
||
---|---|---|---|
Line 45: | Line 45: | ||
age myDog --5 | age myDog --5 | ||
</code> | </code> | ||
+ | |||
+ | |||
+ | Typeclasses are an important concept in haskell. They are very similar in concept to Java interfaces (we implement certain behaviours through them). For example, we might want to convert an entity to a string to print it. This could be done by implementing the Show class. Take a look at its [[https://hackage.haskell.org/package/base-4.18.0.0/docs/src/GHC.Show.html#Show | implementation]]. Notice the ''{-# MINIMAL showsPrec | show #-}''. That tells us that it's enough to implement one of those functions to have a complete implementation. Let's implement show for ''Maybe a'' | ||
+ | |||
+ | <code haskell> | ||
+ | -- The syntax "Show a =>" is called a constraint. This tells us that a also needs to be an instance of Show. Why do we need it here? | ||
+ | instance Show a => Show (Maybe a) where | ||
+ | show Nothing = "Nothing" | ||
+ | show (Just x) = "Just " ++ show x | ||
+ | </code> | ||
+ | |||
+ | In summary, typeclasses allow us to abstract certain behaviours. Some important ones we'll use in this lab are ''Eq''(to specifiy whether 2 things are equal or different) or ''Ord'', to be able to compare type instances. | ||
After the lab, you can take a look at some more cool stuff related to data types in haskell and functional types in general \\ | After the lab, you can take a look at some more cool stuff related to data types in haskell and functional types in general \\ | ||
Line 52: | Line 64: | ||
===== 9.1. Chess Basics ===== | ===== 9.1. Chess Basics ===== | ||
- | **9.1.1** Let's implement a ''ChessResult'' data type. In chess, a game result can be either a Win, a Draw or a Loss. Make sure to add ''deriving Show''. | + | **9.1.1** Let's implement a ''ChessResult'' data type. In chess, a game result can be either a Win, a Draw or a Loss. |
<code haskell> | <code haskell> | ||
- | data ChessResult = ??? deriving Show | + | data ChessResult = ??? |
</code> | </code> | ||
- | **9.1.2** In chess, your performance is rated based on a point system. A win is equivalent to a point, a draw is 0.5 points and a loss is 0 points | + | **9.1.2** We want to be able to show a ''ChessResult''. For this, we could do ''deriving Show'' to let GHC derive the implementation, but let's do it ourselves. |
+ | <code haskell> | ||
+ | instance Show ChessResult where | ||
+ | ??? | ||
+ | </code> | ||
+ | |||
+ | **9.1.3** In chess, your performance is rated based on a point system. A win is equivalent to a point, a draw is 0.5 points and a loss is 0 points | ||
<code haskell> | <code haskell> | ||
points :: ChessResult -> Float | points :: ChessResult -> Float | ||
Line 63: | Line 81: | ||
</code> | </code> | ||
- | **9.1.3** We want to find out the number of point a player earns after playing multiple games. Try to implement this as a closure. | + | **9.1.4** We want to find out the number of point a player earns after playing multiple games. Try to implement this as a closure. |
<code haskell> | <code haskell> | ||
score :: [ChessResult] -> Float | score :: [ChessResult] -> Float | ||
Line 69: | Line 87: | ||
</code> | </code> | ||
- | **9.1.4** Now that we can measure a player's score, we want to establish a ranking. Return the descending list of scores. Hint: ''sort'' | + | **9.1.5** Now that we can measure a player's score, we want to establish a ranking. Return the descending list of scores. Hint: ''sort'' |
<code haskell> | <code haskell> | ||
ranking :: [[ChessResult]] -> [Float] | ranking :: [[ChessResult]] -> [Float] | ||
Line 75: | Line 93: | ||
</code> | </code> | ||
- | **9.1.5** Each tournament also offers a cash prize, based on player performance. Compute the earnings for each player. The formula we'll use is ''player_earnings = player points * prize money / total points'' | + | **9.1.6** Each tournament also offers a cash prize, based on player performance. Compute the earnings for each player. The formula we'll use is ''player_earnings = player points * prize money / total points'' |
<code haskell> | <code haskell> | ||
earnings :: [[ChessResult]] -> Float ->[Float] | earnings :: [[ChessResult]] -> Float ->[Float] | ||
Line 86: | Line 104: | ||
We also want to model a chess tournament. We'll represent it as a binary tree. | We also want to model a chess tournament. We'll represent it as a binary tree. | ||
<code Haskell> | <code Haskell> | ||
- | data player = Player { ??? } | + | data Player = Player { ??? } |
data Tree a = ??? deriving Show | data Tree a = ??? deriving Show | ||
type Tournament = Tree Player | type Tournament = Tree Player | ||
</code> | </code> | ||
- | **9.2.1** After a game, we want to update a player's games with the new result. | + | **9.2.1** We want to be able to compare players based on their performance. In order to do this, we'll have to implement 2 type classes |
+ | <code haskell> | ||
+ | -- Two players are considered equal if their score is the same. Alternatively, 2 players are different if their scores are different | ||
+ | instance Eq Player where | ||
+ | ??? | ||
+ | |||
+ | -- In the same manner, players can be compared based on their score | ||
+ | instance Ord Player where | ||
+ | ??? | ||
+ | </code> | ||
+ | |||
+ | |||
+ | **9.2.2** After a game, we want to update a player's games with the new result. | ||
<code haskell> | <code haskell> | ||
addResult :: ChessResult -> Player -> Player | addResult :: ChessResult -> Player -> Player | ||
Line 97: | Line 127: | ||
</code> | </code> | ||
- | **9.2.2** It's finally time to play some chess. After the game is played, we want to return 2 players with updated results. The first one should be the winner (helpful later). However, everyone knows chess is a luck-based game. As such, a player will win a game if their odds of winning (calculated based on elo difference) is greater than 0.5. | + | **9.2.3** It's finally time to play some chess. After the game is played, we want to return 2 players with updated results. The first one should be the winner (helpful later). However, everyone knows chess is a luck-based game. As such, a player will win a game if their odds of winning (calculated based on elo difference) is greater than 0.5. |
<code haskell> | <code haskell> | ||
-- odds that A beats B based on their elo | -- odds that A beats B based on their elo | ||
Line 107: | Line 137: | ||
</code> | </code> | ||
- | **9.2.3** A guy with a lot of free time decided it would be fun to play games against multiple opponents. Return the updated player and the updated list of opponents (take a look at ''mapAccumL''). | + | **9.2.4** A guy with a lot of free time decided it would be fun to play games against multiple opponents. Return the updated player and the updated list of opponents (take a look at ''mapAccumL''). |
<code haskell> | <code haskell> | ||
multipleMatches :: Player -> [Player] -> (Player, [Player]) | multipleMatches :: Player -> [Player] -> (Player, [Player]) | ||
Line 113: | Line 143: | ||
</code> | </code> | ||
- | **9.2.4** It seems that the chess community is boiling and players are ready to throw hands. Each player must face all other players in a single match. At the end, all players will have their results updated. | + | **9.2.5** It seems that the chess community is boiling and players are ready to throw hands. Each player must face all other players in a single match. At the end, all players will have their results updated. |
<code haskell> | <code haskell> | ||
groupStage :: [Player] -> [Player] | groupStage :: [Player] -> [Player] | ||
Line 119: | Line 149: | ||
</code> | </code> | ||
- | **9.2.5** Playing every player is a bit boring. We want to organize a tournament to decide the best player faster. We can assume each tournament will have 2^n players for simplicity. | + | **9.2.6** Playing every player is a bit boring. We want to organize a tournament to decide the best player faster. We can assume each tournament will have 2^n players for simplicity. |
Each round, groups of 2 players will face each other, with the winner advancing to the next round. Don't forget to update their results. | Each round, groups of 2 players will face each other, with the winner advancing to the next round. Don't forget to update their results. | ||
<code haskell> | <code haskell> | ||
Line 126: | Line 156: | ||
</code> | </code> | ||
- | **9.2.6** Carlos Magnusen wants to organize a chess tournament. As he only wants the best players to participate, he decided to organize a big group stage with all players facing each other | + | **9.2.7** Carlos Magnusen wants to organize a chess tournament. As he only wants the best players to participate, he decided to organize a big group stage with all players facing each other |
a tournament stage with the best 2^n groups players (n must be as high as possible - if 150 players are playing, we expect n to be 7). | a tournament stage with the best 2^n groups players (n must be as high as possible - if 150 players are playing, we expect n to be 7). | ||
- | <code haskell> | ||
- | -- this makes it so you can use sort on a list of players. They'll be ranked based on total points. We'll go into more detail next time. | ||
- | instance Eq Player where | ||
- | p1 == p2 = score (tournamentGames p1) == score (tournamentGames p2) | ||
- | |||
- | |||
- | instance Ord Player where | ||
- | p1 < p2 = score (tournamentGames p1) < score (tournamentGames p2) | ||
- | p1 > p2 = score (tournamentGames p1) > score (tournamentGames p2) | ||
- | p1 <= p2 = score (tournamentGames p1) <= score (tournamentGames p2) | ||
- | </code> | ||
<code haskell> | <code haskell> | ||
chessTournament :: [Player] -> Tournament | chessTournament :: [Player] -> Tournament | ||
Line 144: | Line 163: | ||
</code> | </code> | ||
- | **9.2.7** Carolina wants to see how her beloved Romeo did in his first tournament. However, his rowdy 'chess' friends may have taken him to the local chess bar instead, so he may have not played at all. Implement the function ''findPlayerByName'', to find a player's result in a tournament(1 if he is the winner, 2 if he reached semifinals, 3 for quarterfinals etc) | + | **9.2.8** Carolina wants to see how her beloved Romeo did. However, he sometimes goes to the 'chess' club instead of playing, so he may have not been present at all. Implement the function ''findPlayerScoreByName'', which returns ''Just score'' if he was there or ''Nothing'' if he wasn't |
<code haskell> | <code haskell> | ||
- | findPlayerByName :: String -> Tournament -> Maybe Int | + | findPlayerScoreByName :: String -> Tournament -> Maybe Float |
- | findPlayerByName = ??? | + | findPlayerScoreByName = ??? |
</code> | </code> | ||