178 71 3MB
English Pages 79 Year 2019
Contents 1
We have a problem...
4
2
Haskell Tools
6
3
Simple Values
11
4
Drawing Cards from Strings
18
5
Sorting & Grouping
26
6
Categories of Hands
33
7
Comparing Hands
37
8
Special cases
42
9
Testing the Rankings
50
10 How to find a good hand
55
11 Printing the scores
65
Did you read The Little Schemer by David Friedmann and Matthias Felleisen?
1
Yes. It’s a fascinating book.
What’s fascinating about it?
2
It’s different from every other programming book.
Say more.
3
What’s wrong with lengthy explanations?
4
Most programming books feature some bits of code, surrounded by lots of explanations. This one is different. It’s practically only code, and almost zero explanations.
It’s boring. Plus sometimes the code is not even correct or beautiful.
What’s important for you when you read about programming?
I need to read code, not lengthy explanations. When I see an explanation about the code I just read, it’s a bit as if the author was saying ”If the code could speak, it would say.. blah blah”. 5
What would you yourself say to such an author?
6
Why did you write this book?
7
Who should read this book?
8
Who else?
9
Do you have any hints about how to read the book?
10
Hey... the code can speak!
The Little Schemer aims to teach the concept of recursion. I was wondering if it would be interesting not to teach one concept in particular, but to write a program in that manner, i.e. through a book of questions and answers.
People who know nothing about the Haskell programming language, and don’t like to read lots of explanations.
People who like to read code!
Yes: → Take the time to read everything. → Skimming through chapters will hurt you. → Try things on your computer.
Anything else you want to say?
11 Thank You! to the remarkable friends who helped me write this book: Andrea Chiou, Fr´ed´eric Merizen, Michel Belleville, Jonathan Perret, Rapha¨el Pierquin, and to all the kind persons who asked questions during the hundreds of Dojo sessions where I wrote some Haskell code in front of others. Curiosity is inspiring.
Should we start?
12
3
By all means.
1
We have a problem...
What is this : ”8r” ?
13
A String .
Yes. What does it represent?
14
An eight of hearts, or 8r.
What does ”7♣ 6q 9♠” represent?
15
Some others cards: 7♣, 6q, and 9♠.
Right. And what does ”Ar Kr Qr Jr Tr” represent?
16
It represents victory: it’s a royal flush.
17
A flush.
And with 9♣ Ar K♠ Kq 9q 3♣ 6q ?
18
Two pairs.
Correct. And with A♣ Q♣ K♠ Kq 9q 3♣ ?
19
Nothing, because there are less than seven cards.
And with 9r 5♠ ?
20
Nothing, for the same reason.
What is the best hand we can do with the following: 4q 2q K♠ Kq 9q 3♣ 6q ?
That’s right. What about K♣ 9♠ K♠ Kq 9q 3♣ 6q ?
It’s a full house. Say, why are you showing me all these cards? 21
Because we have a problem, and I wanted to be sure you know the basics about Poker.
22
4
Show me what the problem is.
We have to write a program which, given this input: K♣ 9♣ A♣ 9r 4q 7♠
9♠ Ar Q♣ 5♠ 2q T♠
These are the cards of some players in a game of Texas Hold’em1 . Right? 23
K♠ Kq 9q 3♣ 6q K♠ Kq 9q 3♣ 6q K♠ Kq 9q 3♣ K♠ Kq 9q 3♣ 6q K♠ Kq 9q
– right – ..would output this: K♣ 9♣ A♣ 9r 4q 7♠
9♠ Ar Q♣ 5♠ 2q T♠
24
K♠ Kq 9q 3♣ 6q Full House (winner) K♠ Kq 9q 3♣ 6q Two Pair K♠ Kq 9q 3♣ K♠ Kq 9q 3♣ 6q Flush K♠ Kq 9q
What do you see?
25
Do you think we can solve the problem?
26
1 See
I see.
Some lines are just left as they are. Some lines are marked with the ranking of the best possible hand given the cards on the line. The line with the best ranking is marked as the winner.
http://rubyquiz.com/quiz24.html
5
Yes, I suppose, provided we have good tools.
2
Haskell Tools
What is the effect of this program? main :: IO () main = putStrLn ”42”
It prints the Answer to the Ultimate Question of Life, The Universe, and Everything. It is a very simple Haskell program.
How many functions are defined in this program?
28
What is the type of the function?
29
How do we run this program?
30
27
Only one, called main.
This function is of type IO () , which means it will perform some input/output operations, and yield a void result that we don’t care about.
One way is to save it to a file, for example answer.hs, and then launch runhaskell: runhaskell answer.hs ←42
How do we execute some Haskell code in an interactive way, without having to write a program?
31
One way is to launch ghci: and try things there:
ghci ←-
GHCi, http://www.haskell.org/ghc/ :? for help 6 * 7 ←42 sqrt 2 ←1.4142135623730951 4 > 3 ←True
6
What other tools do we have to solve the problem?
32
Tools that we will code in Haskell, I suppose.
That is right. What is: ’ r’ ?
33
It’s a Char value.
What is: ’ ♠’ ?
34
It’s another Char value.
What is: ” r♣q♠” ?
35
It’s a list of Chars, or String value.
How do you get information about the type of a value?
36
Using :type and :info in ghci:2
:type ’r’ ←’r’ :: Char :type "r♣q♠" ←"r♣q♠" :: [Char] :info String ←type String = [Char]
What is the value of this expression: length ” r♠q♣” ?
37
4.
What is the value of: length [3,2,7,6,8] ?
38
5.
What is the type of: length ” r♠q♣” ?
39
Int .
What is the type of: length ?
40
[a] → Int 3
How does this Haskell expression:
41
length is a function from list of any type, to Int .
42
[ ”time” , ” flies ” , ” like ” , ”an”,”arrow”].
43
It’s a function from String to list of String s:
length :: [a] → Int
read?
What is the value of the expression: words ”time flies like an arrow”
What is the type of: words ?
:type words ←words :: String → [String]
What is the value of: 4 == 5?
44
False.
What is the value of: 4 == 3 + 1?
45
True.
hex values for ’ r’, ’ ♣’, ’ q’ and ’ ♠’ are: 2665, 2663, 2666 and 2660. Using ’ H’, ’ C’, ’ D’ and ’ S’ would also work very well. is true for old versions of GHC only. The type signature of length is actually Foldable t =⇒t a → Int . Foldable types are containers that can be folded into a summary value ( [a] is such a type). 2 Unicode 3 This
7
What is the value of: 4 == 2 ∗ 1 + 1?
46
False.
What is the value of: 4 == 2 ∗ (1 + 1)?
47
True.
What is the type of the expression 4 == 5?
48
It’s a boolean expression:
:type 4 == 5 ←4 == 5 :: Bool
Let’s create incidents. What is the value of: ’ r’ == True?
49
It’s not a valid Haskell expression:
Couldn’t match expected type ‘Char’ with actual type ‘Bool’ In the second argument of ‘(==)’, namely ‘True’ In the expression: ’r’ == True
50 If the first argument of ( == ) is a Char, the second argument should be also a Char.
What do you infer?
What else can you infer?
51
There is a function ( == ) that takes two arguments:
(==) 3 4 ←False
What is the value of: (+) 4 5?
52
9.
What do you conclude?
53
Operators are functions too.
What is the value of expression: 4 + False?
54
Again, it’s not a valid expression:
No instance for (Num Bool) arising from a use of ‘+’ Possible fix: add an instance declaration for (Num Bool) In the expression: 4 + False
But the message is different.
Let’s try to understand the message. What is the type of (+) ?
55
Here some :info about Num:
(+) :: Num a =⇒ a → a → a.
Explain in your own words what the error message is about.
Class Num a where (+) :: a → a → a (*) :: a → a → a (-) :: a → a → a negate :: a → a abs :: a → a signum :: a → a fromInteger :: Integer → a instance Num Integer instance Num Int instance Num Float instance Num Double
8
56 There are 4 types that are instances of the class Num, and Bool is not one of those types.
Therefore the expression:
4 + False
is invalid.
The operator (+) requires operands of a type that is an instance of Num.
Making Bool an instance of Num would make the expression a valid expression. (Although I don’t think it’s a good idea).
What is the value of the expression: subtract 1 10 ?
57
9.
What is the value of: subtract 25 100 ?
58
75.
What is the type of: subtract 1 10 ?
59
Num a =⇒ a.
What is the type of subtract 1 ?
60
Num a =⇒ a → a.
What is the type of subtract ?
61
Num a =⇒ a → a → a.
62
Let’s ask:
Let’s suppose that dec = subtract 1. What is the value of dec 1000?
let dec = subtract 1 ←dec 1000 ←999
We just created a new function.
Let’s create another one: inc = (+) 1. What is the value of: inc (inc (inc 1)) ?
63
Let’s ask again:
let inc = (+) 1 ←inc (inc (inc 1)) ←4
And another one: dbl = (∗) 2. What is the value of dbl 6?
64
I think I know the answer.
let dbl = (*) 2 ←dbl 6 ←12
Good. Now let’s define something new: foo = inc ◦ dbl. What is the value of foo 10?
65
I have no idea. Let’s try:4
let foo = inc ◦ dbl ←foo 10 ←21
Why is the result 21?
66
What is the type of : o ?
67
It is 10 multiplied by 2, plus 1.
:type (◦) ←( ◦ ) :: (b → c) → (a → b) → a → c
That is the most complex function I’ve seen. 4 In
ghci, replace ◦ with . .
9
Suppose we have the following:
68
Interesting!
69
I think it should be something like:
fry :: Batter → Pancake mix :: (Flour,Milk,Egg) → Batter
Then suppose we define a new function: cook = fry ◦ mix
cook :: (Flour, Milk, Egg) → Pancake
What would be the type of this new function?
How would you explain the role of ◦ in this expression: f ◦ g ?
The ◦ function takes two functions f and g and compose them into a new function. Applying this new function to an argument x is the same as applying f to the result of applying g to x. 70
In fewer words?
Suppose we want a new function: wc :: String → Int . The job of this function is to count words in a String .
Create the wc function in ghci.
71
( f ◦ g) x = f (g x)
72
That seems easy to do.
73
Here it is:
let wc = length ◦ words ←wc "a four words sentence" ←4
Perfect! Do you want to solve the problem?
74
10
Let’s make some tea first.
3
Simple Values
How many cards are represented in this String : ”Ar Kr Qr Jr Tr” ?
75
Five.
It gives us 3, but that’s not the exact number of cards in the String .
Does (length ◦ words) s give us the number of cards in s when s is equal to ”Qr #! Jr” ?
76
Why not?
77
Because ”#!” does not represent a valid card.
Does ”Qr” represent a valid card?
78
Yes.
79
Right.
What do we need to do with cards?
80
Compare them, sort them, and group them.
What is the value of ”4r” < ”5♣”?
81
True.
What is the value of ”Tr”< ”A♣”?
82
False.
83
Exactly.
Some values of the type String can represent a valid card, some cannot.
Some comparisons of cards as represented by String work, some don’t.
11
What type could represent valid cards for all values of this type?
What would you call that type?
How would you describe the type Card in your own words?
84
I don’t know. Maybe we need to make our own type.
85
Card.
The type Card is the set of all possible cards in the game of Poker.
86
Can you give examples of values of such a type?
87 Queen of Hearts, Two of Spades, Ace of Clubs, Five of Diamonds.
What should be required for a value to be of the type Card?
88
It should have a rank and suit.
What are the possible ranks?
89
What are the possible suits?
90
Hearts, Spades, Diamonds, Clubs.
Let’s define a new type for the suits.
91
I don’t know how to do that.
92
Yes. It is defined with two boolean values: False,
Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace.
Do you know about the Bool type?
True.
What can we do with Bool values?
93
We can compare them for equality or inequality:
> False == False True > True /= True False > False < True True
How is Bool defined?
94
Like this:
data Bool = False | True
How do you define the type Suit ?
95
Like this, I suppose:
data Suit = Hearts | Spades | Diamonds | Clubs
What should we be able to do with values of type Suit ?
96
Diamonds == Diamonds
97
What does the expression: yield?
Compare them for equality, for instance when looking for a flush in a group of cards.
It’s an error:
No instance for (Eq Suit) arising from a use of ‘==’ Possible fix: add an instance declaration for (Eq Suit)
I don’t understand. 12
What it means is that there are currently no rules for comparing Suit values.
If we declare the type Suit to be an instance of the class Eq, then default rules will be used for comparing.
A value is equal to itself and different from all other values.
We add a deriving clause to the declaration:
98
We didn’t define any.
99
What are those default rules?
100
Obviously this is what we need. How do we do that?
101
Great! Now we can compare values:
> Diamonds == Diamonds True > Diamonds == Clubs False
data Suit = Hearts | Spades | Diamonds | Clubs deriving Eq
Let’s define a new type for the ranks now.
102
I think I know how to do that:
data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace
Why not use numbers for the Rank values from 2 to 10?
Rank is a new data type. We can create values of this type by using data constructors. The symbols 2, 3, 4... are already used to denote numbers, and cannot be used as data constructors. 103
What should we be able do with values of type Rank?
104
Compare them for equality or inequality. For example when looking for a pair or a straight in group of cards, or just sorting the cards by Rank value.
What does the expression: Ten < Jack yield?
105
It’s an error again:
No instance for (Ord Rank) arising from a use of ‘ King ?
108
True.
13
What is the default rule for inequality?
The default rule is that the first value declared is the lower, the next one is greater, and so on. 109
Correct. What is the value of the expression: compare 42 4807 ?
110
LT
111
GT
112
Since we correctly defined our values, it’s: LT.
113
It is EQ.
What about compare Eight Jack ?
114
It is LT.
And compare Five Three ?
115
It is GT.
What is the type of the value returned by compare ?
116
Here’s what ghci tells us:
What is the value of the expression: compare ”TIME” ”FLIES” ?
Right. And of the expression: compare King Ace ?
What is the value of the expression: compare Queen Queen ?
:info compare class Eq a =⇒ Ord a where compare :: a → a → Ordering :info Ordering data Ordering = LT | EQ | GT
Can you explain the compare function in your own words?
compare can compare values of any type which is deriving of both type classes Eq and Ord. The result of the comparison is one of the three values LT, EQ, GT meaning respectively lower than, equal, and greater than. 117
What do we have so far, with regard to representing cards in our poker hand program?
We have our own types Suit , and Rank to represent values that will be used for comparisons. 118
What do we still need?
119
We need to create a type Card to correctly represent cards.
Can you define the type?
120
I’m afraid I can’t. I don’t know yet how to define a type as a combination of other types.
What is the type of the expressions : (’ A’,True), (’ B’,False), (’ E’,True) ?
121
14
(Char, Bool)
What is the type of: (’ A’,True ,’ a ’) ?
122
What do we call such combinations of values?
123
(Char, Bool, Char)
We call them tuples. When formed with two values, we can call them pairs.
How would you describe the type (Char, Bool) in your own words?
124
It’s a set that is a product of all possible values of
Char times all possible values of Bool.
What is the type of the expression: (Queen, Hearts) ?
125
How would you describe the type (Rank, Suit) in your own words?
I think it is: (Rank, Suit)
It’s the set of all possible Ranks times all possible Suit s. It’s the type we are looking for.
126
So how would you define the type Card?
How would you explain such declaration in your own words?
127
Exactly like this: type Card = (Rank, Suit).
128
The type Card is equivalent to a tuple of Rank and
Suit .
What do we want to do with values of type Card?
129
We want to know their Rank value, so that we can search for a pair, a straight, and so on. We also want to know their Suit value, so that we can search for a flush. And and of course we want to extract Card values from String s.
What is the value of the expression: fst (65,’ A’) ?
130
65.
What is the value of the expression: fst (False, 42) ?
131
False
What is the type signature of the function fst ?
132
fst :: (a, b) → a
Can you explain in your own words what this function does?
fst takes a pair as its argument, and yields the first element of the pair. 133
What is the value of the expression: fst (Queen, Hearts) == Queen ?
How would you define a function which takes a Card and gives its Rank, given what we now know about tuples?
134
True, of course.
135
That’s easy:
rank :: Card → Rank rank = fst
15
What is the value of the expression: snd (65,’ A’) ?
136
’ A’.
What is the value of the expression: snd (False, 42) ?
137
42
What is the type of snd ?
138
snd :: (a, b) → b snd takes a pair, and yields the second element of the
pair.
What is the value of the expression: snd (Queen, Hearts) == Hearts ?
Define a function which takes a Card and gives its Suit .
139
True obviously.
140
Here it is:
suit :: Card → Suit suit = snd
Can we determine if two Card values are equals?
141
Let’s try:
> let c = (Queen,Hearts) > let d = (King,Spades) > c == d False > d /= c True
We can. Using your example values, can we tell if c < d ?
142
No we can’t:
No instance for (Ord Suit) arising from a use of ‘ let q = (Queen, Hearts) > let k = (King, Spades) > let a = (Ace, Diamonds) > let j = (Jack, Clubs) > let z = (Ace, Hearts) > rank q < rank k True > rank a > rank j True > rank a == rank z True > suit q /= suit k True > suit q == suit z True
data Suit = Hearts | Spades | Diamonds | Clubs deriving (Eq) data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace deriving (Eq, Ord) type Card = (Rank, Suit) rank :: Card → Rank suit :: Card → Suit rank = fst suit = snd
Listing 1: Card.hs How do we use this? Give some examples.
What does the type Card give us?
147
It gives us values on which we can apply two functions, rank and suit .
What else do we need?
148
A function to create Card values from a String .
→ Use the data construct to create a new type for the values of your specific domain. → Values of a type deriving Eq can be compared for equality with == and /=. → Values of a type deriving Eq,Ord can be compared for inequality with < > = and compare. → You can combine different types into one, using the tuple construct ( , ) . Values of a tuple type can be compared for equality if the types composing the tuple are instance of the class Eq, and the same rule applies for inequality, with the class Ord. → The type construct allows for the definition of a type synonym, as in type String = [Char].
17
4
Drawing Cards from Strings
Here is a simple function definition:
It takes an Integer number, and yields the square of that number. 149
sq :: Integer → Integer sq x = x ∗ x
Can you describe what the function does?
Correct. Here’s another function definition:
150
That is strange.
151
There are several definitions.
foo :: Integer → Integer foo 42 = 42 foo x = x ∗ x
Can you describe it?
What is strange?
How does the function behave?
152 It depends. There are different cases. If the argument is 42 then the function returns back this argument, in any other case, the square of the argument is returned.
Correct. Here’s another example:
It takes two Double floating numbers in a tuple, a dividend and a divisor. If the divisor is zero, then the dividend is discarded (which is why it is represented as a symbol), and the result is zero. In any other case, the result of the division is returned. This function is probably not a very good idea. 153
bar :: (Double, Double) → Double bar ( , 0) = 0 bar (x, y) = x / y
Can you describe what the function does?
18
This way of defining a function is called pattern matching.
Here’s another example:
154
Ok.
155
The second pattern is something new.
156
I see.
sign :: Double → Double sign 0 = 0 sign x | x < 0 = −1 sign = 1
It’s called a guard. It reads: sign of x, when x is lower than zero, equals minus one.
It always returns 1, whatever value we give as an argument.
You have to be careful. Here’s an example of pattern matching gone wrong:
157
dumb :: Double → Double dumb = 1 dumb 0 = 0 dumb x | x < 0 = −1
Listing 2: Dumb.hs Can you guess what it does?
Yes. And the Haskell compiler will do its best to prevent such errors. Here’s what it displayed about the function dumb:
158
I see. You were warned.
159
Okay!
160
That would be nice.
What should be the value of: card ”Qr” ?
161
(Queen, Hearts).
What should be the value of: card ”T♣” ?
162
(Ten, Clubs).
Warning: Pattern match(es) are overlapped In an equation for ‘dumb’: dumb 0 = ... dumb x = ...
There are rules to pattern matching: → Patterns are evaluated top-down. → Patterns should start with the most specific at the top, and end with the more general at the bottom.
Let’s get back to our problem. Suppose we have a new function: card :: String → Card.
19
What should be the value of: card ”4q” ?
163
(Four, Diamonds).
Describe the function card in your own words.
164
card converts a String value into a Card value.
Can you write the function card?
165
No, but I can write its type signature:
card :: String → Card
Let’s pretend for now that the value of these two expressions:
Only this: when given the argument ”Ar”, the function card yields a tuple in which the first element is Ace, and the second element is Hearts. 166
rank (card ”Ar”) == Ace suit (card ”Ar”) == Hearts
is True. What should we infer about the definition of card in that case?
Write this in Haskell.
167
Here:
card :: String → Card card ”Ar” = (Ace, Hearts)
Is the function correct?
What happens if we give card an argument that is different from ”Ar”?
168
I think that it will be, at least for that value: ”Ar”.
169
An error:
>card "7q" == (Seven, Diamonds) Exception: Non-exhaustive patterns in function card
What is another String value that the function should convert?
Update the function definition, so that those two expressions:
170
For example, ”K♣”.
171
Let’s add a pattern, then:
card :: String → Card card ”Ar” = (Ace,Hearts) card ”K♣” = (King,Clubs)
rank (card ”K♣”) == King suit (card ”K♣”) == Spades
yield True.
What else should be true about the function card?
172
Well, the function should be able to successfully convert 50 other String patterns.
Do you remember about words?
173
Of course.
174
It is the list of String s:
What is the value of the expression: words ”2r 7r Kr Ar” ?
[ ”2r”, ”7r”, ”Kr”, ”Ar”]
20
Do you remember about sq?
175
Yes, it’s a function we wrote. sq x = x ∗ x.
What is the value of: [sq 1, sq 2, sq 3] ?
176
That’s [1, 4, 9].
What is the value of: map sq [1,2,3] ?
177
Same answer: [1, 4, 9].
What is the value of: map abs [−4,8,0,−7] ?
178
[4,8,0,7] .
What is the value of: map toUpper ”cat and bat” ?
179
”CAT AND BAT”.
What is the value of: map even [5,8,2,3,7] ?
180
[False,True,True,False,False].
What does: map rank cs yield when
181
[Ace,King,Queen].
cs = [( Ace,Clubs),(King,Hearts),(Queen,Spades)] ?
Can you describe in your own words the function: map :: (a → b) → [a] → [b] ?
map takes a function, a list, and yields a new list resulting of the application of the function on each element in the initial list. 182
What should be the value of the expression: map card (words ”K♣ Ar K♣”) ?
183
[( King,Clubs),(Ace,Hearts),(King,Clubs)].
184
[( King,Clubs),(Ace,Hearts),(King,Clubs)] again.
Right. What about: cards ”Ar Ar” ?
185
[( Ace,Hearts),(Ace,Hearts)].
Describe the function cards in your own words.
186
Here’s a new function: cards : String → [Card]. What should be the value of the expression: cards ”K♣ Ar K♣” ?
The function, when given a String representing cards, yields a list of the Card values matching the patterns in the String .
Write the function cards.
187
Here it is:
cards :: String → [Card] cards s = map card (words s)
What is an equivalent definition of h x = g ( f x) ?
What is an equivalent definition of cards s = map card (words s)?
188
h=g ◦ f
189
The one that uses ( ◦ ) :
cards :: String → [Card] cards = map card ◦ words
21
Back to patterns. What needs to change in the function card so that the expression:
190
Adding more patterns!
card card card card card card
map suit (cards ”Ar A♠ Aq A♣”) == [Hearts ,Spades ,Diamonds ,Clubs]
yields True?
And what needs to change in the function card so that this expression:
191
We need to add even more patterns:
card card card card card card card card card
map rank (cards ”Jr Qr Kr”) ==[Jack, Queen, King]
is True?
:: String → Card ”Ar” = (Ace,Hearts) ”A♠” = (Ace,Spades) ”Aq” = (Ace,Diamonds) ”A♣” = (Ace,Clubs) ”K♣” = (King,Clubs)
:: String → Card ”Jr” = (Jack, Hearts) ”Qr” = (Queen,Hearts) ”Kr” = (King, Hearts) ”Ar” = (Ace, Hearts) ”A♠” = (Ace, Spades) ”Aq” = (Ace, Diamonds) ”A♣” = (Ace, Clubs) ”K♣” = (King, Clubs)
This is getting tedious.
The function would be 52 lines long! We have to find a way to shorten it.
If we should continue to add patterns to the function this way until it can convert every card in the game of Poker, what would happen?
192
What is the most repeated element in the function?
193
The character ’ r’, converted into the value Hearts.
Can we separate what is repeated from what is not?
194
I don’t know how to do that.
195
Right.
A String is a list of Char, you remember? Rewrite the String s as List s.
card card card card card card card card card
Suppose we had a function: charToSuit :: Char → Suit such that
:: String [’ J ’,’ r’] [’ Q ’,’ r’] [’ K ’,’ r’] [’ A ’,’ r’] [’ A ’,’ ♠ ’] [’ A ’,’ q ’] [’ A ’,’ ♣’] [’ K ’,’ ♣’]
→ Card = = = = = = = =
(Jack, Hearts) (Queen,Hearts) (King, Hearts) (Ace, Hearts) (Ace, Spades) (Ace, Diamonds) (Ace, Clubs) (King, Clubs)
196
That would be practical.
197
Here it is:
map charToSuit ”r♠q♣” == [Hearts,Spades,Diamonds,Clubs]
yields True.
Write the function charToSuit.
charToSuit :: Char → Suit charToSuit ’ r’ = Hearts charToSuit ’ ♠’ = Spades
22
charToSuit ’ ♣’ = Clubs
charToSuit ’ q’ = Diamonds
Instead of the values Hearts,Spades,Diamonds,Clubs in the card function, use a call to charToSuit.
198
Ok:
card card card card card card card card card
Now on every line you can replace the Char values on the left and right of the equal sign by a variable.
199
200
→ Card = = = = = = = =
(Jack, charToSuit ’ r’) (Queen,charToSuit ’r’) (King, charToSuit ’ r’) (Ace, charToSuit ’ r’) (Ace, charToSuit ’ ♠’) (Ace, charToSuit ’ q’) (Ace, charToSuit ’ ♣’) (King, charToSuit ’ ♣’)
Surely you mean like this:
card card card card card card card card card
Then eliminate every line that is redundant.
:: String [’ J ’,’ r’] [’ Q ’,’ r’] [’ K ’,’ r’] [’ A ’,’ r’] [’ A ’,’ ♠ ’] [’ A ’,’ q ’] [’ A ’,’ ♣’] [’ K ’,’ ♣’]
:: String [’ J ’, s] = [’ Q’,s] = [’ K’,s] = [’ A’,s] = [’ A’,s] = [’ A’,s] = [’ A’,s] = [’ K’,s] =
→ Card (Jack, charToSuit s) (Queen,charToSuit s) (King, charToSuit s) (Ace, charToSuit s) (Ace, charToSuit s) (Ace, charToSuit s) (Ace, charToSuit s) (King, charToSuit s)
Right:
card card card card card
:: String [’ J ’, s] = [’ Q’,s] = [’ K’,s] = [’ A’,s] =
→ Card (Jack, charToSuit s) (Queen,charToSuit s) (King, charToSuit s) (Ace, charToSuit s)
The function still needs one line for each different Rank value.
Suppose we had a function charToRank :: Char → Rank such that:
201
Like this:
charToRank :: Char → Rank charToRank ’2’ = Two charToRank ’3’ = Three charToRank ’4’ = Four charToRank ’5’ = Five charToRank ’6’ = Six charToRank ’7’ = Seven charToRank ’8’ = Eight charToRank ’9’ = Nine charToRank ’T’ = Ten charToRank ’J’ = Jack charToRank ’Q’ = Queen charToRank ’K’ = King charToRank ’A’ = Ace
map charToRank ”23456789TJQKA” == [Two, Three, Four, Five, Six, Seven, Eight, Nine ,Ten, Jack, Queen, King, Ace]
yields True. How would you define this function?
Now you can use it in the card function, replacing each rank character in the patterns with a variable, then calling charToRank on that variable.
202
Yes. So our card function becomes simpler:
card :: String → Card card [ r ,s] = (charToRank r, charToSuit s)
23
Are we done with reading Cards from String s?
203
I think we are.
→ Use Pattern Matching to define different cases your function has to take into account. → Patterns are evaluated top-down, left to right. → Put the most specific pattern on top of the list. → Use guards to express conditions on selecting a pattern. → Use map to apply a function to a list of values. → Refactor duplicated code by highlighting repetitions in the code.
24
Here are propositions that are true about our functions so far:
204
And here is the code of our functions so far:
module Card where
map suit (cards ”Ar A♠ Aq A♣”) == [Hearts ,Spades ,Diamonds ,Clubs] map rank (cards ”2r 3r 4r 5r 6r 7r 8r 9r Tr Jr Qr Kr Ar”) == [Two, Three, Four, Five ,Six, Seven, Eight, Nine ,Ten, Jack, Queen, King, Ace]
type Card = (Rank, Suit) data Suit = Hearts | Spades | Diamonds | Clubs deriving (Eq) data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace deriving (Eq, Ord) rank :: Card → Rank rank = fst suit :: Card → Suit suit = snd cards :: String → [Card] cards = map card ◦ words card :: String → Card card [ r ,s] = (charToRank r, charToSuit s) charToSuit charToSuit charToSuit charToSuit charToSuit
:: Char → Suit ’ r’ = Hearts ’ ♠’ = Spades ’ q’ = Diamonds ’ ♣’ = Clubs
charToRank :: Char → Rank charToRank ’2’ = Two charToRank ’3’ = Three charToRank ’4’ = Four charToRank ’5’ = Five charToRank ’6’ = Six charToRank ’7’ = Seven charToRank ’8’ = Eight charToRank ’9’ = Nine charToRank ’T’ = Ten charToRank ’J’ = Jack charToRank ’Q’ = Queen charToRank ’K’ = King charToRank ’A’ = Ace
Listing 3: Card.hs
25
5
Sorting & Grouping
What do you call a group of 5 cards in the game of Poker?
How would you define a type Hand in Haskell?
205
It’s called a hand.
206
Like this:
type Hand = [Card]
Unfortunately, we cannot define the type to be limited to lists of 5 Cards only.
Yes: Q♣ Q♠ 4q 4r Ar It’s Two Pairs.
Here’s a hand: 4q Q♣ 4r Q♠ Ar Can you group the cards so that the ranking of the hand is made visible?
207
How did you do that? Explain the steps.
208
First, I sorted the cards by rank: Ar Q♣ Q♠ 4q 4r I grouped cards with the same rank: Ar Q♣ Q♠ 4q 4r then put the highest pair first: Q♣ Q♠ 4q 4r Ar
Here are five other cards: 7r Aq 7♣ Ar 7r Can you do the same steps again?
209
Ok:
Sorting the cards by rank: Aq Ar 7r 7♣ 7♠ then forming groups: Aq Ar 7r 7♣ 7♠ then sorting the groups: 7r 7♣ 7♠ Aq Ar The larger group should be first in the hand. It’s a Full House.
26
That is how you determine the ranking of a hand: you sort the cards, group them, and then you recognize which category of hand you have.
Well, I do all that in my head of course, I don’t need to physically move the cards. 210
How can we do the same in Haskell?
211
What is the value of the expression: sort [4,8,3,2,5] ?
212
What is the value of the expression: sort ”cat pec” ?
213
” accept”.
What is the type of the expression: group [4,8,7,2,9] ?
214
It’s a list of lists of Integers, or [[ Integer ]] .
What is the value of the expression: group [4,8,7,2,9] ?
215
[[4],[8],[7],[2],[9]]
What is the value of the expression: group ”hello ” ?
216
[ ”h”, ”e”, ” ll ” , ”o”] .
What is the value of: group ”abracadabra” ?
217
[ ”a”, ”b”, ” r ” , ”a”, ”c” , ”a”, ”d”, ”a”, ”b”, ” r ” , ”a”] .
218
[ ”aaaaa”,”bb”,”c” , ”d”, ” rr ” ] .
219
sort the list before applying group on it.
What about the expression: (group ◦ sort ) s when s = ”abracadabra” ?
What do we need to have proper grouping of values through the list?
I have no idea.
[2,3,4,5,8] .
What is the value of: (group ◦ sort )
[4,8,5,4,2] ?
220
What is the value of: (group ◦ sort )
[4,8,8,4,4] ?
221
[[4,4,4],[8,8]] .
What is the value of: (group ◦ sort )
[3,8,8,4,4] ?
222
[[3],[4,4],[8,8]] .
What do we have so far?
223
Here’s a list of interesting numbers:
224
.
[[2],[4,4],[5],[8]] .
We have groups of elements of the same value, but those groups are not always arranged in the same order, i.e. with the larger group first in the list.
[1, 2, 3, 5, 6, 7, 11, 13, 28, 496]. Exactly the result that we would get with sort .
ns = [28, 7, 6, 496, 5, 3, 13, 2, 1, 11]. What does the expession: sortBy compare ns yield?
Why is it so?
Because sortBy will use compare for sorting the list, thus following the order defined for Integer values. 225
27
Here’s a bizarre function: bizarre bizarre bizarre bizarre
226
[2, 6, 28, 496, 1, 3, 5, 7, 11, 13].
:: Integer → Integer → Ordering i j | (even i ) == (even j ) = compare i j i j | (even i ) = LT i j | otherwise = GT
Can you guess the result of: sortBy bizarre ns ?
Can you explain the effect of bizarre on sortBy ?
227
The bizarre function sorts even and odd numbers apart.
What is the type of sortBy ?
228
Can you explain what the function sortBy does?
229
sortBy :: (a → a → Ordering) → [a] → [a] .
sortBy takes a comparison function, a list, and yields a list that is sorted according to the criterion given by the comparison function.
What comparison function would you give to sortBy if we wanted to have the list in descending order?
230
A function that flips the arguments to compare:
flipcompare i j = compare j i
> sortBy flipcompare ns [496, 28, 13, 11, 7, 6, 5, 3, 2, 1]
See, it works!
What is div 32 8 ?
231
4.
What is div 8 32 ?
232
0.
What is flip div 8 32 ?
233
4.
What does: flip compare 1 2 yield?
234
GT.
What does: flip compare 10 0 yield?
235
LT.
Can you describe the function:
236
flip takes a function, and two arguments for that function, and applies that function with the two arguments in reverse order.
flip :: (a → b → c) → b → a → c ?
How to sort any list in descending order?
237
What would be another way to sort a list of numbers in descending order?
Use sortBy ( flip compare) instead of sort .
That would be by comparing the opposite of each value instead of the value itself: 238
> let neg i j = compare (-i) (-j) > sortBy neg ns [496, 28, 13, 11, 7, 6, 5, 3, 2, 1]
28
What is the value of: compare ”bird”,”cat” ?
239
LT, because ’ b’ < ’c’ .
What is the value of: comparing length ”bird” ”cat” ?
240
GT, because ” bird ” is longer than ”cat” .
What is the value of: negate 496 ?
241
−496.
242
Like this:
How would you sort our list by descending order without the neg helper?
> sortBy (comparing negate) ns [496, 28, 13, 11, 7, 6, 5, 3, 2, 1]
What is the value of the expression: sortBy compare [”aaa”,”d”,”bb”, ”cc”] ?
243
That’s obvious: [ ”aaa”,”bb”, ”cc”, ”d”] .
244
It’s [ ”d”, ”bb”, ”cc”, ”aaa”].
245
The same thing as: compare (p i) (p j ) .
And what would flip (comparing p) i j yield?
246
The same thing as: flip compare (p j) (p i ) .
What is the value of:
247
It is [ ”aaa”,”bb”, ”cc”, ”d”] .
248
I have no idea.
What is the value of: compare (1,”z”) (3, ”aaa”) ?
249
LT, because 1 < 3.
What is the value of: compare (2,”cc”) (2, ”aa”) ?
250
GT, because 2 == 2 and ”cc” > ”aa”.
Suppose that the following expressions are true:
251
Here it is:
What is the value of: sortBy (comparing length) [”aaa”,”d”, ”bb”, ”cc”]
What does the function: comparing :: Ord a =⇒(b → a) → b → b → Ordering
yield when given a function p, as in: comparing p i j ?
sortBy ( flip (comparing length)) [ ”aaa”,”d”, ”bb”, ”cc”]
What function should we give to comparing so that the comparison operates first on the length of the groups, then on the values in the group itself?
lg ”aa” == (2, ”aa”) lg ”z” == (1, ”z”) lg ”dddd” == (4, ”dddd”)
lg :: [a] → ( Int ,[ a]) lg xs = (length xs, xs)
Write the function lg .
What is the value of:
252
sortBy (comparing lg) [ ”aaa”,”d”, ”bb”, ”cc”]
29
[ ”d”, ”bb”, ”cc”, ”aaa”].
And what is the value of:
253
[ ”aaa”,”cc”, ”bb”, ”d”] .
sortBy ( flip (comparing lg)) [ ”aaa”,”d”, ”cc”, ”bb”]
What is the value of: sortBy ( flip (comparing lg))
254
[[8,8],[4,4],[2]] .
[[2],[4,4],[8,8]]
Is this what we are looking for?
Yes. Larger groups are first in the list, and groups of same size are sorted by decreasing value of elements in the group. 255
What would be a better name than flip in the context of comparing and sorting?
256
Maybe descending:
descending :: (a → b → c) → b → a → c descending = flip
What is the value of:
257
[[8,8],[4,4],[2]] .
sortBy (descending (comparing lg)) [[2],[4,4],[8,8]]
Do you remember the types and functions we defined before?
258
Yes!
259
map rank s
type Hand = [Card] type Card = (Rank, Suit) rank = fst
Given that s == cards ”T♠ Kq K♣ Q♣ Tq” what expression could give us the ranks of the cards represented by s?
will yield [Ten, King, King, Queen, Ten].
Given that rs == [Ten,King,King,Queen,Ten] what expression could give us a list of the groups of ranks in rs?
260
group (sort rs)
will yield [[ Ten,Ten],[Queen],[King,King]].
Given that gs == [[ Ten,Ten],[Queen],[King,King]] what expression could give us the groups in gs sorted by decreasing size of group and value of rank?
261
sortBy (descending (comparing lg))gs
will yield [[ King,King ],[ Ten,Ten],[Queen]].
Write the function groups :: Hand → [[Rank]]. The following expressions should be True:
262
Here it is:
groups :: Hand → [[Rank]] groups = sortBy (descending (comparing lg)) ◦ group ◦ sort ◦ map rank
groups (cards ”T♠ Kq K♣ Q♣ Tq”) == [[ King,King ],[ Ten,Ten],[Queen]] groups (cards ”T♠ 4q 4♣ 4♣ 4q”) == [[ Four,Four,Four,Four],[Ten]] groups (cards ”T♠ Kq K♣ T♣ Tq”) == [[ Ten,Ten,Ten],[King,King]]
30
Are we done with grouping cards?
263
I think yes.
→ You can sort a list of a by any criteria c :: a → a → Ordering you give to sortBy. → Use compare x y if you want to compare x and y on the order defined by their type. → Use comparing f x y if you want to compare the values obtained by applying f to x and y. → Use flip f to reverse the arguments of f before applying f .
31
Here are assertions that hold true about our functions:
264
groups (cards ”T♠ Kq K♣ Q♣ Tq”) == [[ King,King ],[ Ten,Ten],[Queen]] groups (cards ”T♠ 4q 4♣ 4♣ 4q”) == [[ Four,Four,Four,Four],[Ten]] groups (cards ”T♠ Kq K♣ T♣ Tq”) == [[ Ten,Ten,Ten],[King,King]]
And here is our code:
module Hands where import Data.List import Data.Ord import Cards type Hand = [Card] groups :: Hand → [[Rank]] groups = sortBy (descending (comparing lg)) ◦ group ◦ sort ◦ map rank lg :: [a] → ( Int , [a]) lg xs = (length xs,xs) descending :: (a → b → c) → b → a → c descending = flip
Listing 4: Hands.hs
32
6
Categories of Hands
Why do we sort and group the ranks?
265
So that we find what category of hand we have.
What are the different categories?
266
Here they are:
Royal Flush Straight Flush Four of a Kind Full House Flush Straight Three of a Kind Two Pairs One Pair High Card
What are those values: High Card, One Pair, etc.?
267
How would you describe the type Category ?
268
Can you define the type Category?
269
Ar Kr Qr Jr 10r 10♠ 9♠ 8♠ 7♠ 6♠ 6♠ 6r 6♣ 6q K♠ Jr J♠ Jq A♠ A♣ Aq 10q 8q 5q 4q Q♣ J♠ 10q 9♠ 8r 4♠ 4r 4♣ Aq Jq Q♠ Qr 5♣ 5♠ J♣ Kq Kr 9r 8♠ 2♣ Q♠ Jq 9r 8♣ 3♠
They are categories of ranking of a hand.
It’s the set of all possible categories of rankings in the game of Poker.
Here it is:
data Category = HighCard | OnePair | TwoPairs | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Eq, Ord)
33
The type is deriving Eq and Ord. Why is it so?
270
So That we can compare the values.
271
HighCard.
272
OnePair.
273
TwoPairs.
274
ThreeOfAKind.
275
FullHouse.
276
FourOfAKind.
What is the grouping pattern in a HighCard?
277
That’s 5 groups of 1 card.
What is the grouping pattern of a FullHouse?
278
That’s one group of 3, and one group of 2.
What is the value of lengths gs when
279
[3,2] .
280
[1,1,1,1,1] .
What is the value of the expression: findCategory h when h = cards ”Qr J♣ 9♠ 8q 3r” ? What is the value of the expression: findCategory h when h = cards ”8r 8♣ Q♠ 9q 3r” ? What is the value of the expression: findCategory h when h = cards ”Qr Q♣ 8♠ 8q 3r” ? What is the value of the expression: findCategory h when h = cards ”8r 8♣ 8♠ Qq 3r” ? What is the value of the expression: findCategory h when h = cards ”8r 8♣ 8♠ Qq Qr” ? What is the value of the expression: findCategory h when h = cards ”8r 8♣ 8♠ 8q 9r” ?
gs = [[ Eight,Eight,Eight ],[ Queen,Queen]] ?
What is the value of lengths gs when gs = [[ Queen],[Jack],[Nine ],[ Eight ],[ Three]] ?
Write the function lengths.
281
Easy:
lengths :: [[ a ]] → [ Int ] lengths = map length
What is the value of categorize [1,1,1,1,1] ?
282
HighCard.
What is the value of categorize [4,1] ?
283
FourOfAKind.
34
Write the function categorize :: [ Int ] → Category. It should detect the categories:
284
OK:
HighCard OnePair TwoPairs ThreeOfAKind FullHouse FourOfAKind
categorize categorize categorize categorize categorize categorize categorize
Great! Now write the function:
285
findCategory :: Hand → Category.
:: [ Int ] → [1,1,1,1,1] [2,1,1,1] [2,2,1] [3,1,1] [3,2] [4,1]
Category = HighCard = OnePair = TwoPairs = ThreeOfAKind = FullHouse = FourOfAKind
Here it is:
findCategory :: Hand → Category findCategory = categorize ◦ lengths ◦ groups
The following expression should be true: let hs = [ ”4♠ 5q K♣ T♣ 3q” , ”4♠ Kq K♣ T♣ 3q” , ”4♠ Kq K♣ T♣ Tq” , ”T♠ Kq K♣ T♣ Tq” , ”T♠ Kq K♣ K♣ 8q” , ”T♠ Kq K♣ K♣ Kq”] in map (findCategory ◦ cards) hs == [HighCard ,OnePair ,TwoPairs ,FullHouse ,ThreeOfAKind ,FourOfAKind]
Make categorize and lengths helpers function to findCategory.
286
OK.
findCategory :: Hand → Category findCategory = categorize ◦ lengths ◦ groups where categorize :: [ Int ] → Category categorize [1,1,1,1,1] = HighCard categorize [2,1,1,1] = OnePair categorize [2,2,1] = TwoPairs categorize [3,1,1] = ThreeOfAKind categorize [3,2] = FullHouse categorize [4,1] = FourOfAKind lengths :: [[ a ]] → [ Int ] lengths = map length
Are we done with groups of ranks?
287
Yes. Let’s take a break. I’ll show you card tricks.
→ Pattern matching applied on lists can spare us the writing of complex conditionals. → Use the where clause inside the definition of a function to add local functions. → The form let g = x in f g is equivalent to f g where g = x
35
Here are the assertions about our code:
288
groups (cards ”T♠ Kq K♣ Q♣ Tq”) == [[ King,King ],[ Ten,Ten],[Queen]] groups (cards ”T♠ 4q 4♣ 4♣ 4q”) == [[ Four,Four,Four,Four],[Ten]] groups (cards ”T♠ Kq K♣ T♣ Tq”) == [[ Ten,Ten,Ten],[King,King]]
And here’s the code we have so far:
module Hands where import Cards import Data.List (group,sort,sortBy) import Data.Ord(comparing) type Hand = [Card]
let hs = [ ”4♠ 5q K♣ T♣ 3q” , ”4♠ Kq K♣ T♣ 3q” , ”4♠ Kq K♣ T♣ Tq” , ”T♠ Kq K♣ T♣ Tq” , ”T♠ Kq K♣ K♣ 8q” , ”T♠ Kq K♣ K♣ Kq”] in map (findCategory ◦ cards) hs == [HighCard ,OnePair ,TwoPairs ,FullHouse ,ThreeOfAKind ,FourOfAKind]
data Category = HighCard | OnePair | TwoPairs | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Eq, Ord) findCategory :: Hand → Category findCategory = categorize ◦ lengths ◦ groups where categorize :: [ Int ] → Category categorize [1,1,1,1,1] = HighCard categorize [2,1,1,1] = OnePair categorize [2,2,1] = TwoPairs categorize [3,1,1] = ThreeOfAKind categorize [3,2] = FullHouse categorize [4,1] = FourOfAKind lengths :: [[ a ]] → [ Int ] lengths = map length groups :: Hand → [[Rank]] groups = sortBy ( flip (comparing lg)) ◦ group ◦ sort ◦ map rank where lg g = (length g, g)
Listing 5: Hands.hs
36
7
Comparing Hands
What does this represent: Qr Qq 10♣ 10♠ Jr ?
289
It’s a hand in the Poker game.
Is it a good hand?
290
Better than nothing, but not the best ranking.
What is the ranking of that hand?
291
Two Pairs, of Queen and Ten.
292
It also has a ranking of Two Pairs, of Queen and Jack.
Which hand is the best?
293
The second one.
Why is that?
294
A pair of Jacks is higher than a pair of Tens.
Does this hand: 4♠ 4r 4♣ Aq Jq beat this hand: Q♠ Qr 5♣ 5♠ J♣ ?
295 Yes. The first hand ranking is Three of a Kind, the second is Two Pairs.
Does this hand: Q♠ Qr 5♣ 5♠ J♣ beat this hand: Q♠ Qr 3♣ 3♠ J♣ ?
296 Yes. Both hands are Two Pairs, and the higher pair is the same, but the second pair in the first hand beats the second pair in the second hand.
Here is another hand: Qr Qq J♣ Jr 10♠
Does this hand: 9♠ 9r 9♣ Aq Qq beat this hand: 9♠ 9r 9♣ Aq Tq ?
Yes. Both hands are Three of a Kind, but the last remaining card in the first hand beats the last remaining card in the second hand.
297
37
What is the rule for comparing two hands with different categories of groups?
The hand with the higher category beats the hand with the lower category. 298
What is the rule for comparing two hands with same category?
The hand with the highest rank of cards in the groups beats the hand with lower ranks in the groups. 299
What is the rule for comparing two hands with same category and same groups?
The hand with the highest rank of remaining cards (kickers) beats the hand with lower rank of kickers. 300
What do we need in order to compare two hands on their ranking?
We need to find the category of each hand and also the ranks of the cards. 301
Define the type Ranking.
302
Here it is:
type Ranking = (Category,[Rank])
What can we do with values of type Ranking?
303 We can compare them according to the rules of Texas Hold’em rankings. We can know their Category, such as FullHouse, or TwoPairs.
Do you remember the functions we designed so far?
304
Quite well. Those are: rank :: Card → Rank suit :: Card → Suit card :: String → Card cards :: String → [Card] findCategory :: Hand → Category groups :: Hand → [[Rank]]
Let’s suppose we have a function:
305
Let’s suppose.
306
LT because OnePair < ThreeOfAKind.
ranking :: Hand → Ranking
that we use to compare hands.
What should the expression: comparing ranking g h yield when: g = cards ”Qr Qq T♣ 6♠ 2♠” h = cards ”2r 2q T♣ 6♠ 2♠” ?
What should the expression: comparing ranking h i yield when:
LT because ThreeOfAKind == ThreeOfAKind and [Two,Two,Two,Ten,Six] < [Two,Two,Two,Ten,Seven]. 307
h = cards ”2r 2q T♣ 6♠ 2♠” i = cards ”2r 2q T♣ 7♠ 2♠”?
What should be the value of ranking g ?
308
(OnePair,[Queen,Queen,Ten,Six,Two]).
What should be the value of ranking h ?
309
(ThreeOfAKind,[Two,Two,Two,Ten,Six]).
38
What should be the value of ranking i ?
Can you define the function: ranking :: Hand → Ranking ?
310
(ThreeOfAKind,[Two,Two,Two,Ten,Seven]).
311
I know how to write the Category part, but not the
[Rank] part: ranking :: Hand → Ranking ranking h = (findCategory h, ...)
How should we arrange the Ranks in the second part of a Ranking value?
In such a way that we can compare Ranking values of the same Category: The Ranks from the larger groups come first. 312
What function do we currently have that could give us Ranks arranged like that?
We have a function groups :: Hand → [[Rank]]. But it yields a list of lists of Ranks not a list of Ranks. 313
What is the value of the expression: groups (cards ”Tr Qq T♣ 5♠ T♠”) ?
314
It’s [[ Ten,Ten,Ten ,],[ Queen],[Five]].
315
[Ten,Ten,Ten,Queen,Five].
316
That’s right.
317
Good.
What is the value of: concat [ ”cat” , ”and”,”bat” ] ?
318
”catandbat”.
What is the value of: concat [[1,2],[3,4]] ?
319
[1,2,3,4] .
What would be the Rank list for the groups: [[ Ten,Ten,Ten],[Queen],[Five]] ?
So you need a function from [[ Rank]] to [Rank].
Here’s a function that has this signature: concat :: [[ a ]] → [a] .
Can you complete the definition? The following assertion should be true:
320
Yes:
ranking :: Hand → Ranking ranking h = (findCategory h, concat (groups h))
let h = cards ”Ar Kr Qr Jr 8♣” −− highest HighCard g = cards ”2r 2♠ 3♠ 4q 5r” −− lowest OnePair in comparing ranking h g == LT
Can you write the functions:
321
category :: Ranking → Category ranks :: Ranking → [Rank]
That’s very easy:
category :: Ranking → Category category = fst ranks :: Ranking → [Rank] ranks = snd
such that following assertions hold true: let r = ranking(cards ”T♠ Kq K♣ Q♣ Tq”) in (category r == TwoPairs) && (ranks r == [King,King,Ten,Ten,Queen])
39
What do we have so far?
322
We have several functions: → cards Converting a String into a list of Card values, → rank, suit Knowing the Rank and Suit of a Card, → ranking Computing the Ranking of a Hand, → category Knowing the Category of a given Ranking, → ranks Knowing the Ranks of a given Ranking.
What other functions do we have?
323
We have auxiliary functions, like groups, descending,
findCategory.
Rearrange the code so that these functions are helpers to the essential functions.
324
Ok.
type Ranking = (Category,[Rank]) category = fst ranks = snd ranking :: Hand → Ranking ranking h = (findCategory h, concat(groups h)) where findCategory :: Hand → Category findCategory = categorize ◦ lengths ◦ groups where categorize :: [ Int ] → Category categorize [1,1,1,1,1] = HighCard categorize [2,1,1,1] = OnePair categorize [2,2,1] = TwoPairs categorize [3,1,1] = ThreeOfAKind categorize [3,2] = FullHouse categorize [4,1] = FourOfAKind lengths :: [[ a ]] → [ Int ] lengths = map length groups :: Hand → [[Rank]] groups = sortBy (descending (comparing lg)) ◦ group ◦ sort ◦ map rank where lg :: [a] → ( Int ,[ a]) lg xs = (length xs,xs) descending :: (a → b → c) → b → a → c descending = flip
→ Use concat to concatenate lists together. → Attach helper functions to the public functions once they are correct.
40
Here are the properties that hold true about our functions:
325
And here’s the code:
module Hands where import Data.List import Data.Ord import Cards
let h = cards ”Ar Kr Qr Jr 8♣” −− highest HighCard g = cards ”2r 2♠ 3♠ 4q 5r” −− lowest OnePair in comparing ranking h g == LT let r = ranking(cards ”T♠ Kq K♣ Q♣ Tq”) in (category r == TwoPairs) && (ranks r == [King,King,Ten,Ten,Queen])
type Hand = [Card] data Category = HighCard | OnePair | TwoPairs | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Eq, Ord) type Ranking = (Category,[Rank]) category = fst ranks = snd ranking :: Hand → Ranking ranking h = (findCategory h, concat(groups h)) where findCategory :: Hand → Category findCategory = categorize ◦ lengths ◦ groups where categorize :: [ Int ] → Category categorize [1,1,1,1,1] = HighCard categorize [2,1,1,1] = OnePair categorize [2,2,1] = TwoPairs categorize [3,1,1] = ThreeOfAKind categorize [3,2] = FullHouse categorize [4,1] = FourOfAKind lengths :: [[ a ]] → [ Int ] lengths = map length groups :: Hand → [[Rank]] groups = sortBy (descending (comparing lg)) ◦ group ◦ sort ◦ map rank where lg :: [a] → ( Int ,[ a]) lg xs = (length xs,xs) descending :: (a → b → c) → b → a → c descending = flip
Listing 6: Hands.hs
41
8
Special cases
What is a Straight? Give an example.
It’s a category of hand where the ranks follow a sequence, as in Tq 9♣ 8r 7♣ 6♠. 326
What does the expression ranking h yield when h = cards ”Tq 9♣ 8r 7♣ 6♠” ?
327
This, which is wrong:
(HighCard, [Ten,Nine,Eight,Seven,Six])
What should it be?
Can you write a function from Ranking to Ranking, which when given a HighCard with ranks from Ten to Six, yields a Straight ?
promote. This should be true: promote (ranking (cards ”Tq 9♣ 8r 7♣ 6♠”)) == ( Straight , [Ten,Nine,Eight,Seven,Six])
328
( Straight , [Ten,Nine,Eight,Seven,Six]).
329
What should I call the function?
330
Ok:
promote :: Ranking → Ranking promote (HighCard, [Ten,Nine,Eight,Seven,Six]) = ( Straight , [Ten,Nine,Eight,Seven,Six])
What should the function return when it is given a Ranking with category other than HighCard ?
It should return the Ranking value unchanged, rather than promoted to a Straight . 331
Right. Add that case as a new pattern. This should be true:
332
Easy:
promote :: Ranking → Ranking promote (HighCard,[Ten,Nine,Eight,Seven,Six]) = ( Straight ,[ Ten,Nine,Eight,Seven,Six]) promote r = r
promote (ranking (cards ”Tq T♣ 8r 7♣ 6♠”)) == (OnePair, [Ten,Ten,Eight,Seven,Six])
42
What patterns should be added so that promote detects all possible Straights?
333
All HighCards with a sequence:
from King to Nine, from Queen to Eight, from Jack to Seven, ... until Six toTwo And there are special cases: from Five to Ace as a low card, and from Ace to Ten, which is the RoyalFlush.
Can you add all those patterns?
334
I suppose.. But this is tedious:
promote :: Ranking → Ranking promote (HighCard,[Ten,Nine,Eight,Seven,Six]) = ( Straight ,[ Ten,Nine,Eight,Seven,Six]) promote (HighCard,[Nine,Eight,Seven,Six,Five]) = ( Straight ,[ Nine,Eight,Seven,Six,Five]) promote r = r
I did it only for two Straights, and I’m already tired.
Can you simplify the patterns?
Can you write a helper function isStraight :: [[ Rank]] → Bool that would dectect a Straight once and for all?
335
I don’t know how.
336
I see what you mean:
promote :: Ranking → Ranking promote (HighCard, rs) | isStraight rs = ( Straight , rs) promote r = r where isStraight :: [Rank] → Bool isStraight [Ten, , , ,Six] = True isStraight [Nine, , , ,Five] = True isStraight = False
Now you can add all the other patterns for Straight.
337
How would you define isStraight with less code?
338
Again, that’s tedious. There must be another way.
By subtracting the lowest Rank from the highest Rank. If the difference is 4, then we have a Straight. isStraight :: [Rank] → Bool isStraight [h, , , , l ] = (h−l) == 4 isStraight = False
You cannot subtract Rank values. They are not numbers.
Do you remember about values of the type Ordering?
Here’s some info about Ordering:
339
That’s too bad.
340
Yes: LT, EQ, GT.
instance Enum Ordering instance Eq Ordering instance Ord Ordering
> :info Ordering data Ordering = LT | EQ | GT instance Bounded Ordering
43
Here’s some info about Enum: > :info Enum class Enum a where succ :: a → a pred :: a → a toEnum :: Int → a fromEnum :: a -> Int enumFrom :: a → [a] enumFromThen :: a → a → [a] enumFromTo :: a → a → [a] enumFromThenTo :: a → a → a → [a]
341
I’m curious about Enum.
342
Interesting! I have to try some things:
> fromEnum 1 > fromEnum 0 > fromEnum 2 > toEnum 1 EQ > toEnum 0 LT
Change the definition of the Rank type to make it derive
343
Enum.
EQ LT GT :: Ordering :: Ordering
That’s simple:
data Rank = Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King | Ace deriving (Eq, Ord, Enum)
What is the value of: fromEnum Two ?
344
0.
What is the value of: fromEnum Ace ?
345
12.
Can you explain in your own words what
346
fromEnum converts a data type value to an Int value. In the case of the Rank data type, fromEnum yields the number corresponding to the order of the values in the declaration.
fromEnum :: enum a ==> a → Int
does ?
What is the result of:
347
map fromEnum [King,Queen,Ten,Nine,Eight] ?
44
[12,11,10,9,8] .
Rewrite the definition of isStraight . These assertions should be true:
348
OK:
isStraight :: [Rank] → Bool isStraight [h, , , , l ] = (fromEnum h) − (fromEnum l) ==4
let hs = [ ”Tq 9♣ 8r 7♣ 6♠” , ”Kq Q♣ Jr T♣ 9♠” , ”8q 7♣ 6r 5♣ 4♠”] in map (category ◦ promote ◦ ranking ◦ cards) hs == [ Straight , Straight , Straight ]
What is the value of ranking cs when cs = cards ”T♠ Jq A♣ Q♣ Kq” ?
A Ranking value with the wrong Category: (HighCard,[Ace,King,Queen,Jack,Ten]).
349
What is the value of promote (ranking cs) when cs = cards ”T♠ Jq A♣ Q♣ Kq” ?
A corrected value of Ranking: ( Straight ,[ Ace,King,Queen,Jack,Ten]).
350
What is the value of promote (ranking cs) when cs = cards ”5♠ 4q A♣ 3♣ 2q” ?
This is the Straight with lower Ace. That case is not correctly handled yet.
351
(HighCard,[Ace,Five,Four,Three,Two])
Can you adapt the promote function to that case? This should be true:
352 Yes. In that case we have to rearrange the Rank list for correct comparison with other Straight rankings:
promote(ranking(cards ”Ar 5♣ 4r 3♠ 2q”)) == ( Straight ,[ Five,Four,Three,Two,Ace])
promote :: Ranking → Ranking promote (HighCard,[Ace,Five, , , ]) = ( Straight ,[ Five,Four,Three,Two,Ace]) promote (HighCard, rs) | isStraight rs = ( Straight , rs) promote r = r
What is the role of the promote function with regard to the ranking function?
It’s a helper function. All Ranking values computed by ranking should pass through promote.
353
Can we change the ranking function so that it promotes correct rankings to Straights? Here’s what should be true:
The solution is to use promote as the last computation step in ranking:
354
ranking :: Hand → Ranking ranking h = promote (category h, concat (groups h))
let g = cards ”Aq 2♣ 3♠ 5q 4r” h = cards ”6q 2♣ 3♠ 5q 4r” in (category.ranking) g == Straight && comparing ranking g h == LT
Great! Now for the last Category of Rankings.
355
Okay!
What is the ranking of this hand: 5q Qq 8q Jq 2q ?
356
It’s a Flush.
How can you tell?
357
All the cards have the same suit.
45
What is the value of the expression: isFlush cs when cs = cards ”5q Qq 8q 3q 2q” ? What is the value of: isFlush cs when cs = cards ”5q Qq 8q 3q 2♣” ?
358
That should be: True.
359
False.
Describe the function isFlush :: Hand → Bool.
360
isFlush tells if all the cards in a hand have the same Suit .
How would you design the function isflush ?
361
I don’t know. But it contains map suit for sure.
isFlush :: Hand → Bool isFlush = ... ◦ map suit
What can be done with a list of [ Suit ] to find whether they are all the same?
362
Apply a function allSame to the list:
isFlush :: Hand → Bool isFlush = allSame . map suit
Now we have to create a function: allSame :: Eq(a) =⇒[a] → Bool.
Yes. We can use group. If we group all the elements in the list, and have only one group, then allSame is True. 363
Can you write the function allSame ? Make it an auxiliary function for isFlush.
364
Okay.
isFlush :: Hand → Bool isFlush = allSame . map suit where allSame :: Eq(a) =⇒[a] → Bool allSame xs = length (group xs) == 1
What is the type of ( == ) ?
365
It’s a function:
( == ) :: Eq a =⇒a → a → Bool.
What is the type of: ( == ) 4 ?
366
It’s a function too:
( == ) :: (Eq a, Num a)=⇒a → Bool.
What is the value of: (> 0) 4 ?
367
True.
What is the value of: ( == 42) 54 ?
368
False.
What is the value of: (2 ∗) 17 ?
369
34.
370
That’s good to know.
Those expressions using an operator or a function with only half of the arguments needed are examples of sections.
46
How would you write allSame using a section?
371
Like this:
isFlush :: Hand → Bool isFlush = allSame . map suit where allSame :: Eq(a) =⇒[a] → Bool allSame = (==1) ◦ length ◦ group
What do you call a High Card in which all suits are the same?
What do you call a Straight in which all suits are the same?
What do you call a Straight of Ace in which all suits are the same?
Can you design a function qualify , which transforms a Ranking according to those rules, the way that promote transforms a Ranking according to the rules of determining a Straight ?
372
A Flush.
373
A Straight Flush.
374
A Royal Flush.
This qualify function should require information from isFlush as well as the Ranking. 375
Then its type should be:
376
Ok.
qualify :: Bool → Ranking → Ranking, and we will pass it the result of isFlush.
Let’s start with the case when isFlush returns False.
In this case, the result is the initial ranking, unchanged. 377
qualify :: Bool → Ranking → Ranking qualify False r = r
This is the default case.
That’s right. The next rule is: a status True with a HighCard should be changed into a Flush
378
OK.
qualify :: Bool → Ranking → Ranking qualify False r = r qualify True (HighCard,rs) = (Flush,rs)
A status True with a Straight , with the first Rank being an Ace should be changed into a RoyalFlush.
379
Ok:
qualify :: Bool → Ranking → Ranking qualify False r = r qualify True (HighCard,rs) = (Flush,rs) qualify True ( Straight ,[ Ace,King,Queen,Jack,Ten]) = (RoyalFlush,[Ace,King,Queen,Jack,Ten])
This pattern you used forStraight Ace is verbose.
380 Yes, but I need to bind the values after the Ace so that I can put them back in the new Ranking.
47
Here’s an operator for lists: (:) :: a → [a] → [a].
381
That’s fine.
What is the value of: 1:[2,3] ?
382
[1,2,3] .
What is the value of: ’ a ’:’ b ’: ”cde”?
383
”abcde”.
You can change the pattern, using the : operator.
384
OK:
qualify qualify qualify qualify
Finally, a status True with a simple Straight should be changed into a StraightFlush.
385
Right.
qualify qualify qualify qualify qualify
Can we adapt the ranking function again so that it detects Flush hands? These tests should pass:
386
:: Bool → Ranking → Ranking False r = r True (HighCard,rs) = (Flush,rs) True ( Straight ,Ace:rs) = (RoyalFlush,Ace:rs)
:: Bool → Ranking → Ranking False r = r True (HighCard,rs) = (Flush,rs) True ( Straight ,Ace:rs) = (RoyalFlush,Ace:rs) True ( Straight , rs) = (StraightFlush, rs)
Yes:
ranking :: Hand → Ranking ranking h = qualify (isFlush h) (promote (findCategory h,concat (groups h)))
let g = cards ”8q Qq 3q Tq 4q” h = cards ”8♣ Q♣ J♣ T♣ 9♣” i = cards ”Ar Qr Jr Tr Kr” in (category.ranking) g == Flush && (category.ranking) h == StraightFlush && (category.ranking) i == RoyalFlush
Are we done now with comparing hands?
387
I think we are!
→ You can use partial applications of functions as you would use a function. → Use sections to make partial application of binary operators. → Use the list construction operator (:) to distinguish elements in a list pattern.
48
Here are the assertions that are true about our code.
388
let value = ranking ◦ cards maxHighCard = value ”Ar K♣ Q♠ Jq 9r” minOnePair = value ”2r 2♠ 3♠ 4q 5r” maxOnePair = value ”Ar A♠ K♠ Qq Jr” minTwoPair = value ”2r 2♠ 3♠ 3q 5r” maxTwoPair = value ”Ar A♠ K♠ Kq Jr” minThreeOfAKind = value ”2r 2♠ 2q 3q 4r” maxThreeOfAKind = value ”Ar A♠ Aq Q♣ Kr” minFullHouse = value ”2r 2♣ 2♠ 3r 3♠” maxFullHouse = value ”Ar A♣ A♠ Kr K♠” minFourOfAKind = value ”2r 2♠ 2q 2♣ 4r” in
Here’s the code so far:
module Hands where import Data.List import Data.Ord import Cards type Hand = [Card] data Category = HighCard | OnePair | TwoPairs | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Eq, Ord) type Ranking = (Category,[Rank]) category = fst ranks = snd
maxHighCard < minOnePair && maxOnePair < minTwoPair && maxTwoPair < minThreeOfAKind && maxThreeOfAKind < minFullHouse && maxFullHouse < minFourOfAKind
ranking :: Hand → Ranking ranking h = qualify (isFlush h) (promote (findCategory h, concat (groups h))) where findCategory = categorize ◦ lengths ◦ groups where categorize :: [ Int ] → Category categorize [1,1,1,1,1] = HighCard categorize [2,1,1,1] = OnePair categorize [2,2,1] = TwoPairs categorize [3,1,1] = ThreeOfAKind categorize [3,2] = FullHouse categorize [4,1] = FourOfAKind lengths = map length groups = sortBy (descending (comparing lg)) ◦ group ◦ sort ◦ map rank where lg :: [a] → ( Int ,[ a]) lg xs = (length xs,xs)
let hs = [ ”4♠ 5q 6♣ 7♣ 8q” , ”A♠ 5q 4♣ 3♣ 2q” , ”4♠ 6♠ K♠ 9♠ T♠” , ”T♠ 9♠ 8♠ 7♠ 6♠” , ”Ar Kr Qr Jr Tr”] in map (category.ranking.cards) hs == [ Straight , Straight ,Flush ,StraightFlush ,RoyalFlush]
descending :: (a → b → c) → b → a → c descending = flip promote (HighCard,[Ace,Five, , , ]) = ( Straight ,[ Five,Four,Three,Two,Ace]) promote (HighCard, rs) | isStraight rs = ( Straight , rs) promote r = r isStraight [h, , , , l ] = (fromEnum h) − (fromEnum l) ==4 qualify qualify qualify qualify
False r = r True (HighCard,rs) = (Flush,rs) True ( Straight ,Ace:rs) = (RoyalFlush,Ace:rs) True ( Straight , rs) = (StraightFlush, rs)
isFlush = allSame . map suit where allSame :: Eq(a) =⇒[a] → Bool allSame = (==1) ◦ length ◦ group
Listing 7: Hands.hs
49
9
Testing the Rankings
How confident are you that our ranking function is correct?
I’m not sure it is correct. I have to read the code again. 389
What would be a good test of this function?
390
What would need to be true for us to say that the function is correct?
I don’t know.
That the rankings we compute conform to the rules of Texas Hold’em. 391
Can we verify that for every possible hand?
392
What can we do then?
393
What is a good sample for our test?
394
Of course not. That would be millions of hands.
We can compute the rankings on a sample of hands that we know are in ranking order, and then verify that the list is ordered.
For each category of ranking, have the lowest and the highest possible values.
Suppose that we have that sample in s, how de we compute r , the rankings of these hands?
That is easy: r = map (ranking ◦ cards) s. That list should be ordered by Ranking value.
395
50
Let’s write that sample, and the rankings.
396
Ok.
let s = [ ”7♠ 5♣ 4q 3q 2♣” −− lowest High Card , ”A♠ K♣ Qq Jq 9♣” −− highest High Card , ”2r 2q 5♣ 4♣ 3♣” −− lowest Pair , ”Ar Aq K♣ Q♣ J♣” −− highest Pair , ”2♣ 2♠ 3♠ 3♣ 4r” −− lowest Two Pairs , ”A♣ A♠ K♠ K♣ Jr” −− highest Two Pairs , ”2r 2q 2♣ 4♣ 3♣” −− lowest Three Of A Kind , ”Ar Aq A♣ Q♣ J♣” −− highest Three Of A Kind , ”5r 4♠ 3q 2♣ Ar” −− lowest Straight , ”Ar K♠ Qq J♣ Tr” −− highest Straight , ”7♣ 5♣ 4♣ 3♣ 2♣” −− lowest Flush , ”A♣ K♣ Q♣ J♣ 9♣” −− highest Flush , ”2r 2q 2♣ 3r 3♣” −− lowest Full House , ”Ar Aq A♣ Kr K♣” −− highest Full House , ”2♣ 2♠ 2r 2q 3♣” −− lowest Four Of A Kind , ”A♣ A♠ Ar Aq K♣” −− highest Four Of A Kind , ”5♣ 4♣ 3♣ 2♣ A♣” −− lowest Straight Flush , ”Ar Kr Qr Jr Tr”] −− Royal Flush r = map (ranking ◦ cards) s
How will we know that the r list is in order?
397
Each element of the list is smaller than the next one.
398
True.
What is the value of: isStrictlyOrdered ”CAT” ?
399
False.
What is the value of: isStrictlyOrdered ”BOOK” ?
400
False.
Define the function
401
What is the value of the expression: isStrictlyOrdered [1,2,3] ?
in your own words.
The function isStrictlyOrdered when given a list of elements of a type that is comparable, yields True if each element is smaller than the next one.
Can you write the function isStrictlyOrdered ?
402
I don’t think so.
403
a < b, b < c, and c < d.
The operator for that is (&&).
404
So, a < b && b < c && c < d has to be True.
What is the value of: and [True, True, True] ?
405
True.
What is the value of: and [True, False, True] ?
406
False.
isStrictlyOrdered :: Ord(a) =⇒[a] → Bool
What needs to be true for isStrictlyOrdered [a,b,c,d] to be True?
51
How can you apply the function: and :: [Bool] → Bool to our a,b,c,d problem?
407
Using it in the test:
and [a < b, b < c, c < d] should yield True.
What else do we need to write the isStrictlyOrdered function?
We need to compare each element of the list with the next one. 408
Can you identify two different lists: one for ”each element” and one for ”next ones” ?
I guess so. We need to compare elements [a,b,c] with elements [b,c,d] respectively. 409
What is the value of the expression tail l when l == [4,8,0,7] ?
410
It is the list [8,0,7] .
What is the value of: tail ”FACTOR”?
411
”ACTOR”. This is what we need to compare the lists.
What is the value of: zip ”ABC” [1,2,3] ?
412
[(’ A’,1) ,(’ B’,2) ,(’ C’,3) ] .
What about: zip [4,8,0,7] [3,2,7,6,8] ?
413
[(4,3) ,(8,2) ,(0,7) ,(7,6) ] .
That’s interesting.
Describe the function zip :: [a] → [b] → [( a,b)] .
The function zip when given two lists, yields a list of pairs. Each pair is made with the relative element of the first and second list respectively. The resulting list has the same length as the shortest of the input lists.
414
How would you produce the pairs of numbers to be compared if we wanted to check the order in the list l ?
415
Like this:
zip l ( tail l )
What is the result of: zip l ( tail l ) when l == [4,8,0,7] ?
416
Is it what we need?
417
Can you write that function? Call the function lt .
418
It is [(4,8) ,(8,0) ,(0,7) ] .
Yes, but we need also a function to compare the values in a pair.
Ok.
lt :: Ord(a) =⇒(a,a) → Bool lt (x,y) = x < y
What if you map that function over the list of pairs [(4,8) ,(8,0) ,(0,7) ] ?
What does using and on this result give?
419
It yields [True,False,True].
420
and [True, False, True] yields False. The list [4,8,0,7]
is not ordered.
52
Can you write the function isStrictlyOrdered ?
421
Yes:
isStrictlyOrdered :: Ord(a) =⇒[a]→ Bool isStrictlyOrdered l = and (map lt (zip l ( tail l ) ) )
It’s a bit compicated, but it works!
We can actually simplify our function a bit. Given what you know about zip , can you guess what the expression: zipWith (∗) [1,2,3] [4,5,6] yields?
422
I guess it is: [4,10,18] .
423
[ ”BUG”,”COG”,”DAG”].
What is the value of zipWith (>) [1,2,3] [0,4,2] ?
424
[True, False, True].
Describe the function
425
What is the value of the expression: zipWith (:) ”BCD” [”UG”,”OG”,”AG”] ?
in your own words.
zipWith when given a binary function and two lists, yields a list of the results of the function applied to each relative element in the input lists.
Can you write isStrictlyOrdered using zipWith ?
426
zipWith :: (a → b → c) → [a] → [b] → [c]
Yes:
isStrictlyOrdered :: Ord(a) =⇒[a]→ Bool isStrictlyOrdered l = and (zipWith (isStrictlyOrdered [4,8,0,7] False > isStrictlyOrdered [1,2,3,4] True > isStrictlyOrdered "BEG" True > isStrictlyOrdered "BUT" False
Can we test our sample now?
428
I think so, yes.
→ Use tail on any non empty list to extract all the elements of the list except the first one. → Use and to determine if a list of Bool contains only True values. → Use zip to assemble the elements of two separate lists into a list of pairs. → Use zipWith to execute a function over the elements of two separate lists.
53
Here are the assertions that hold about our code so far:
429
let s = [ ”7♠ 5♣ 4q 3q 2♣” −− lowest High Card , ”A♠ K♣ Qq Jq 9♣” −− highest High Card , ”2r 2q 5♣ 4♣ 3♣” −− lowest Pair , ”Ar Aq K♣ Q♣ J♣” −− highest Pair , ”2♣ 2♠ 3♠ 3♣ 4r” −− lowest Two Pairs , ”A♣ A♠ K♠ K♣ Jr” −− highest Two Pairs , ”2r 2q 2♣ 4♣ 3♣” −− lowest Three Of A Kind , ”Ar Aq A♣ Q♣ J♣” −− highest Three Of A Kind , ”5r 4♠ 3q 2♣ Ar” −− lowest Straight , ”Ar K♠ Qq J♣ Tr” −− highest Straight , ”7♣ 5♣ 4♣ 3♣ 2♣” −− lowest Flush , ”A♣ K♣ Q♣ J♣ 9♣” −− highest Flush , ”2r 2q 2♣ 3r 3♣” −− lowest Full House , ”Ar Aq A♣ Kr K♣” −− highest Full House , ”2♣ 2♠ 2r 2q 3♣” −− lowest Four Of A Kind , ”A♣ A♠ Ar Aq K♣” −− highest Four Of A Kind , ”5♣ 4♣ 3♣ 2♣ A♣” −− lowest Straight Flush , ”Ar Kr Qr Jr Tr”] −− Royal Flush
And here is our code:
module Hands where import Data.List import Data.Ord import Cards type Hand = [Card] data Category = HighCard | OnePair | TwoPairs | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Eq, Ord, Show) type Ranking = (Category,[Rank]) category = fst ranks = snd ranking :: Hand → Ranking ranking h = qualify (isFlush h) (promote (findCategory h, concat (groups h))) where findCategory = categorize ◦ lengths ◦ groups where categorize :: [ Int ] → Category categorize [1,1,1,1,1] = HighCard categorize [2,1,1,1] = OnePair categorize [2,2,1] = TwoPairs categorize [3,1,1] = ThreeOfAKind categorize [3,2] = FullHouse categorize [4,1] = FourOfAKind lengths = map length groups = sortBy (descending (comparing lg)) ◦ group ◦ sort ◦ map rank where lg :: [a] → ( Int ,[ a]) lg xs = (length xs,xs)
r = map (ranking ◦ cards) s isStrictlyOrdered :: Ord(a) =⇒[a]→ Bool isStrictlyOrdered l = and (zipWith (4) [4,8,0,7] yield?
433
[8,7] .
What about: filter even [4,8,0,7] ?
434
[4,8,0] .
What does:
435
[ ”take” , ” that ” , ”long” ] .
((== 4) ◦ length) (words ”take words that are 4 letters long”)
yield?
What does the expression: choose 2 [1,2,3] yield?
436
[[1,2],
What does the expression: choose 1 [1,2,3] yield?
437
[[1], [2], [3]] .
55
[1,3], [2,3]] .
What does the expression: choose 3 ”abcd” yield?
438
Describe the function choose in your own words.
439
Write the function choose :: Int → [a] → [[ a ]] .
440
[ ”abc”, ”abd”, ”acd”, ”bcd”].
The function choose, when given a number n and a list, yields all the subsequences of that list that contain n elements.
Here it is:
choose :: Int → [a] → [[ a ]] choose n = filter (( == n) ◦ length) ◦ subsequences
Can you try it?
441
Let’s try it:
>choose 5 "ABCDEFG" ["ABCDE","ABCDF","ABCEF","ABDEF","ACDEF" ,"BCDEF","ABCDG","ABCEG","ABDEG","ACDEG" ,"BCDEG","ABCFG","ABDFG","ACDFG","BCDFG" ,"ABEFG","ACEFG","BCEFG","ADEFG","BDEFG" ,"CDEFG"]
What is the value of: maximum [4,8,0,7] ?
What is the value of: maximum ws when ws = words ”time flies like an arrow” ?
And what is: maximumBy (comparing length) ws ?
What is the value of: bestRanking cs when cs = cards ”3r Qr 4r Q♣ A♠ K♠ 7r” ? What is the value of: bestRanking cs when cs = cards ”3r Qr 4r Q♣ Q♠ K♠ 7r” ? Can you write the function: bestRanking :: [Card] → Ranking ?
442
It’s 8.
443
”time”.
444
”arrow”.
445
(OnePair, [Queen,Queen,Ace,King,Seven])
446
(ThreeOfAKind, [Queen,Queen,Queen,King,Seven])
447
Of course:
bestRanking :: [Card] → Ranking bestRanking = maximum ◦ map ranking ◦ choose 5
What is the best hand you can make with these cards? 8♣ Qr 4q
448
You can’t make a hand.
What about: 8♣ Qr 4q 6♠ A♣ Jr ?
449
You can’t either.
What is the rule?
450
Having less than seven cards means the player folded.
56
What is the value of: bestRanking cs when cs = cards ”3r Qr 4r A♠ K♠ 7r” ?
451
(HighCard,[Ace,King,Queen,Seven,Four])
This is wrong. It should not yield a valid Ranking, because there’s only 6 cards, meaning the player folded.
What is the value of: bestRanking cs when cs = cards ”3r Qr 4r A♠” ?
452
This doesn’t work:
*** Exception: maximum: empty list
because there are no possible lists of 5 cards, so the list of Rankings that is given to maximum is empty.
What should the result of bestRanking be when the number of cards in the list is not seven?
It should be nothing. It shouldn’t be a Ranking, at any rate. 453
So what is the return type for the function bestRanking?
454
I really don’t know.
Here is a lookup table:
455
That is cool.
What does lookup ”banana” fruits yield?
456
Just 2.3
What does lookup ”orange” fruits yield?
457
Just 0.8
What does lookup ”pear” fruits yield?
458
Nothing.
Here is information about lookup and its result type:
459
let fruits = [( ”apple”, 1.23) ,( ”banana”,2.3) ,( ”orange”,0.8)]
lookup searches for a certain key in list of pairs. If the key is found, the data associated with the key is returned prefixed with the word Just. If the key is not found, the value Nothing is returned.
lookup :: Eq a =⇒a → [(a, b)] → Maybe b data Maybe a = Nothing | Just a
Can you explain in your own words what lookup does?
Here’s a function:
460
divide :: Double → Double → Maybe Double divide x 0 = Nothing divide x y = Just (x / y)
It safely divides double precision numbers:
>divide 42 4 Just 10.5 >divide 42 0 Nothing
Can you guess what it does?
Can you write a function
461
findRanking :: [Card] → Maybe Ranking ?
Ok.
findRanking :: [Card] → Maybe Ranking findRanking cs | length cs /= 7 = Nothing | otherwise = Just (bestRanking cs)
These assertions should hold: findRanking (cards ”8q Q♣ J♣ T♣ 9♣ Qr Jr”) == Just ( Straight , [Queen,Jack,Ten,Nine,Eight]) findRanking(cards ”8q Q♣ J♣”) ==Nothing
57
What is the type of the values returned by findRanking?
It’s Maybe Ranking. It means that findRanking will try to find the best Ranking from the given list of Cards, or return Nothing if the list is not valid for ranking. 462
What should be done with the Rankings we find for the lists of Cards?
Compare them and determine which list makes the best Ranking. 463
What is the value of: compare (Just 4) (Just 5) ?
464
LT.
What is the value of: compare (Just 4) Nothing ?
465
GT.
What is the value of: compare Diamonds Spades ?
466
You can’t compare Suit values because this type isn’t deriving (Ord).
What is the value of: compare (Just Diamonds) Nothing ?
You can’t compare Maybe Suit values, for the same reason. 467
Can we compare values of a type Maybe a ?
468
It depends on the type a.
Can we compare values of type Ranking ?
469
We certainly can.
470
I remember that game.
What does maximum (map findRanking game) yield when
Just (FullHouse, [King,King,King,Nine,Nine])
game = map cards ls ls = [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q ” , ”9♣ Ar K♠ Kq 9q 3♣ 6q ” , ”A♣ Q♣ K♠ Kq 9q 3♣ ” , ”9r 5♠ ” , ”4q 2q K♠ Kq 9q 3♣ 6q ” , ”7♠ T♠ K♠ Kq 9q ”]
What does maximum (map findRanking game) yield when
471
Just (Flush, [King,Nine,Six,Four,Two]).
game = map cards ls ls = [ ”9♣ Ar K♠ Kq 9q 3♣ 6q ” , ”9r 5♠ ” , ”4q 2q K♠ Kq 9q 3♣ 6q ”]
, ”7♠ T♠ K♠ Kq 9q ”]
What does maximum (map findRanking game) yield when game = map cards ls ls = [ ”K♣ 9♠ K♠ Kq 9q ” , ”9♣ Ar K♠ Kq 9q” , ”A♣ Q♣ K♠ Kq 9q” , ”9r 5♠ ” , ”4q 2q K♠ Kq 9q”
58
472
Nothing. All the players folded.
Do you remember about this output? K♣ 9♣ A♣ 9r 4q 7♠
9♠ Ar Q♣ 5♠ 2q T♠
473
Of course.
K♠ Kq 9q 3♣ 6q Full House (winner) K♠ Kq 9q 3♣ 6q Two Pair K♠ Kq 9q 3♣ K♠ Kq 9q 3♣ 6q Flush K♠ Kq 9q
How is it organized?
474
It looks like there are three columns; the first column states the cards each player was dealt with, the second states the category of hand the player made if any, and the third one indicates if the player is the winner of the game.
Give a name for each of these 3 columns.
475
showdown, categories, winning.
476
Then the two players are indicated as winners.
What if there are two players with exactly the same, best ranking?
What if all players folded and there is no winner?
477
That is not really possible, but I suppose that the winners column would be empty.
What is the result of winning l when
478
[False, True, False, False]
479
[False, True, False, True]
480
[False, False, False, False].
l = [Just 4, Just 8, Nothing, Just 7]
What is the result of winning l when l = [Just 4, Just 7, Nothing, Just 7]
What is the result of winning l when l = [Nothing, Nothing, Nothing, Nothing]
What is the result of winning rs when
481
[True ,False ,False ,False ,False ,False]
rs = [Just (FullHouse, [King,King,King,Nine,Nine]) ,Just (TwoPairs, [King,King,Nine,Nine,Ace]) ,Nothing ,Nothing ,Just (Flush, [King,Nine,Six,Four,Two]) ,Nothing]
59
What does the function
For each element in the list, winning states if the element is or is not the maximum of the list, except if Nothing is actually the maximum of the list, in which case the list contains only Falses. 482
winning :: Ord(a) =⇒[Maybe a] → [Bool]
compute over the list given as an argument?
Write the function
483
winning :: Ord(a) =⇒[Maybe a] → [Bool]. The following
OK.
winning :: Ord(a) =⇒[Maybe a] → [Bool] winning rs = map wins rs where wins Nothing = False wins r = r == maximum rs
assertions should be true: let rs = [Just 4, Just 10, Nothing] in winning rs == [False, True, False] let rs = [Just 8, Just 8, Nothing] in winning rs == [True, True, False] let rs = [Nothing, Nothing, Nothing] :: [Maybe Int] in winning rs == [False, False, False]
What can we do with the winning function?
484
We can fill the third column of our results for a given game.
What should we fill the second column with?
485
What is the result of categories rs when
486
It should be filled with the categories of the rankings for each player.
rs = [Just (FullHouse, [King,King,King,Nine,Nine]) ,Just (TwoPairs,[King,King,Nine,Nine,Ace]) ,Nothing ,Nothing ,Just (Flush,[King,Nine,Six,Four,Two]) ,Nothing]
It’s
[Just FullHouse ,Just TwoPairs ,Nothing ,Nothing ,Just Flush ,Nothing]
Can you write the function:
487
I don’t think so.
488
We map a function of type (a → b) over the list of a.
What function do we need to map here?
489
A function from Maybe Ranking to Maybe Category.
Let’s call this function
490
It returns Nothing:
maybeCategory :: Maybe Ranking → Maybe Category. What does the function return when given Nothing ?
maybeCategory :: Maybe Ranking → Maybe Category maybeCategory Nothing = Nothing
categories :: [Maybe Ranking] → [Maybe Category] ?
What do we use to compute a list of values of type b from a list of values of type a ?
What does the function return when given another value, like for example: Just (FullHouse, [King,King,King,Nine,Nine]) ?
491 It returns Just followed by the category of the ranking, for example: Just FullHouse.
60
Can you complete the function?
492
Yes:
maybeCategory :: Maybe Ranking → Maybe Category maybeCategory Nothing = Nothing maybeCategory (Just r) = Just (category r)
Does it work?
493
It works:
>maybeCategory (Just (FullHouse,[Two,Two,Two, Four,Four]) Just FullHouse >maybeCategory Nothing Nothing
What if we had to write a function from Maybe Ranking to Maybe [Rank] ?
494
Then I suppose we could write this:
maybeRanks :: Maybe Ranking → Maybe [Rank] maybeRanks Nothing = Nothing maybeRanks (Just r) = Just (ranks r)
Could we generalize a bit more?
Compare the two functions maybeCategory and maybeRanks.
495
I don’t see how.
496
Here they are:
maybeCategory :: Maybe Ranking → Maybe Category maybeCategory Nothing = Nothing maybeCategory (Just r) = Just (category r) maybeRanks :: Maybe Ranking → Maybe [Rank] maybeRanks Nothing = Nothing maybeRanks (Just r) = Just (ranks r)
What if we made a general function to map any function (a → b) over a value of type Maybe a in order to obtain a value of type Maybe b?
Call it maybeMap.
497
What would you call that function?
498
That would be something like:
maybeMap :: (a→ b) → Maybe a → Maybe b maybeMap Nothing = Nothing maybeMap f (Just x) = Just (f x)
Try it.
499
Ok.
>maybeMap even (Just 4) Just True >maybeMap even (Just 3) Just False >maybeMap even Nothing Nothing > let r = Just (FullHouse,[Ten,Ten,Ten,Nine, Nine]) > maybeMap category r Just FullHouse
It works!
61
We just rewrote the fmap function: fmap :: Functor f =⇒(a → b) → f a → f b
500
It’s a typeclass for things that can be mapped over:
I don’t know what a Functor is.
501 Thus Maybe is a Functor, so we can fmap a function on a Maybe a and get a Maybe b.
class Functor f where fmap :: (a → b) → f a → f b instance Functor (Either a) instance Functor Maybe instance Functor [ ] instance Functor IO instance Functor ((→ ) r) instance Functor ((,) a)
Yes. What is, for example, fmap (+1) (Just 2) ?
Lists are also an instance of Functor. What is fmap (∗10) [1,2,3] ?
Can you write the function categories? Here’s a test: let rs = [Just (FullHouse, [Ten,Ten,Ten,Four,Four]) ,Nothing] in categories rs == [Just FullHouse, Nothing]
502
It’s Just 3.
503
It’s [10,20,30], just like what map yields, in fact.
504
Here it is:
categories :: [Maybe Ranking] → [Maybe Category] categories = map (fmap category)
You remember about our 3 columns?
505
Yes. showdown, categories and winning.
Given that showdown is defined like this:
506
This:
sd = map cards ( [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q” , ”9♣ Ar K♠ Kq 9q 3♣ 6q” , ”A♣ Q♣ K♠ K♠ 9♠ 3♣” , ”9r 5♠” , ”4q 2q K♠ Kq 9q 3♣ 6q” , ”7♠ T♠ K♠ Kq 9q”])
[( Just FullHouse, True) ,( Just TwoPairs, False) ,( Nothing, False) ,( Nothing, False) ,( Just Flush, False) ,( Nothing, False)]
and using the categories and winning function we have defined, what should yield the expression: scores sd?
Write the function:
507
scores :: [Card] → [( Maybe Category,Bool)].
Here it is:
scores :: [[ Card]] → [( Maybe Category,Bool)] scores sd = zip (categories rs) (winning rs) where rs = map findRanking sd
What do we have so far?
We have functions to calculate the best possible hand from a list of cards, to determine which list of cards has the winning hand. We are almost done! 508
62
→ Use a Maybe a type when your function should give sometimes a value of type a, sometimes no value at all. → You can fmap a function over a Maybe a value, just like you can map a function over a list. → Both Maybe and [ ] are functors i.e. types such that one can map a function over their values. → The general way to map a function over a value of a type of the class Functor, is to use fmap.
63
Here are the tests we can use to verify our code:
509
findRanking (cards ”8q Q♣ J♣ T♣ 9♣ Qr Jr”) == Just ( Straight ,[ Queen,Jack,Ten,Nine,Eight])
And here is our code:
module Scores where import Data.List import Data.Ord import Cards import Hands
findRanking(cards ”8q Q♣ J♣”) ==Nothing let rs = [Just 4, Just 10, Nothing] in winning rs == [False, True, False]
choose :: Int → [a] Aˆ → [[a ]] choose n = filter (( == n) ◦ length) ◦ subsequences
let rs = [Just 8, Just 8, Nothing] in winning rs == [True, True, False] let rs = [Nothing, Nothing, Nothing] :: [Maybe Int] in winning rs == [False, False, False]
bestHand :: [Card] → Hand bestHand cs = maximumBy (comparing ranking) (choose 5 cs)
let rs = [Just (FullHouse,[Ten,Ten,Ten,Four,Four]) ,Nothing] in categories rs == [Just FullHouse, Nothing]
bestRanking :: [Card] → Ranking bestRanking = maximum ◦map ranking ◦choose 5 findRanking :: [Card] → Maybe Ranking findRanking cs | length cs == 7 = Just (bestRanking cs) | otherwise = Nothing
let sd = map cards ( [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q” , ”9♣ Ar K♠ Kq 9q 3♣ 6q” , ”A♣ Q♣ K♠ K♠ 9♠ 3♣” , ”9r 5♠” , ”4q 2q K♠ Kq 9q 3♣ 6q” , ”7♠ T♠ K♠ Kq 9q”]) in scores == [( Just FullHouse, True) ,( Just TwoPairs, False) ,( Nothing, False) ,( Nothing, False) ,( Just Flush, False) ,( Nothing, False)]
winning :: Ord(a) =⇒[Maybe a]Aˆ → [Bool] winning rs = map wins rs where wins Nothing = False wins r = r == maximum rs
categories :: [Maybe Ranking] → [Maybe Category] categories = map (fmap category) scores :: [[ Card]] → [( Maybe Category,Bool)] scores sd = zip (categories rs) (winners rs) where rs = map findRanking sd
Listing 9: Scores.hs
64
11
Printing the scores
Is our problem solved?
510
Almost. The last thing we need to do is write a program that will print the scores for a given game.
Where does the game information come from?
511
It comes from the standard input of the program.
Where does the score information go?
512
It goes to the standard output of the program.
Here’s a little program
513
Let’s try it:
main = interact id
runhaskell Hector Hello World ˆD Hello World
Listing 10: Hector.hs Can you guess what it does?
It just seems to copy its input onto its output.
Here is another one:
514
main = interact reverse
Let’s try it, too:
runhaskell Lancelot TAB BUT ˆD TUB BAT
Listing 11: Lancelot.hs
It outputs the reverse of its input.
65
What about this one:
515
main = interact (show ◦ length)
OK.
runhaskell Ogier tab but dog cat ˆD 16
Listing 12: Ogier.hs
It outputs the number of characters read from input.
Can you explain in your own words what does?
interact , when given a certain function, reads the input stream into a String , applies that function to that String and outputs the result.
What does interact return ?
517
Here’s another interact program:
518
516
interact :: (String → String) → IO ()
It returns nothing. It just changes the current state of the input and output flows.
Yes. It will print the number of lines rather than the number of Chars in input.
main = interact (show ◦ length ◦ lines )
Listing 13: LaHire.hs
runhaskell LaHire tab but dog cat ˆD 4
Can you guess what it does?
What is the effect of this one:
519
main = interact (show ◦ map reverse ◦ lines )
reverse each line from the input.
runhaskell Judith TAB BUT ˆD ["TUB","BAT"]
Listing 14: Judith.hs
But the output is not simple Chars any more, rather it is printed as a list.
Try this one:
520
main = interact (unlines ◦ map reverse ◦ lines )
Ok.
runhaskell Pallas TAB BAT BUT TUB ˆD
Listing 15: Pallas.hs
This time each line is reversed. These are interesting tricks!
What is the value of the expression: show False ?
521
66
It is ”False”.
What is the value of: show (42, True) ?
522
”(42, True)” .
Can you describe what the function:
523
Yes. For any value of a type that is deriving the class
show :: Show a =⇒a → String
Show, show returns a String equivalent of that value.
does?
How would you define the class Show?
It is the class of the types that have values that can be shown as a String . 524
Let’s go back to our 3 columns. Do your remember what they are?
Given that showdown is defined like this: sd = map cards ( [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q” , ”9♣ Ar K♠ Kq 9q 3♣ 6q” , ”A♣ Q♣ K♠ K♠ 9♠ 3♣” , ”9r 5♠” , ”4q 2q K♠ Kq 9q 3♣ 6q” , ”7♠ T♠ K♠ Kq 9q”])
525
Yes: showdown, categories and winning.
526
This:
[( Just FullHouse, True) ,( Just TwoPairs, False) ,( Nothing, False) ,( Nothing, False) ,( Just Flush, False) ,( Nothing, False)]
What are the resulting scores?
Can we give this result to the interact function?
No. These are the results, but not in the form we want them printed. 527
What should be printed?
528 The category should be printed in English, i.e. Full House instead of FullHouse, and if winner is True, then (winner) should be printed.
What is the value of the expression: [1,2] ++ [3,4] ?
529
It is [1,2,3,4] .
What is the value of ”car” ++ ” rot ” ?
530
” carrot ” .
What is the type of the function scores ?
531
It is: [[ Card]] → [( Maybe Category, Bool)].
532
That is easy:
How would you define a type for a score for a given line?
type Score = (Maybe Category, Bool)
If sc is a value of type Score and sc = (Just FullHouse, True), what is the value of show sc?
533
It is an error:
No instance for (Show Category) arising from a use of ‘show’ Possible fix: add an instance declaration for (Show Category)
How can we make the type Category an instance of the
534
Show class?
By adding a deriving clause:
data Category = HighCard | OnePair | TwoPairs | ThreeOfAKind | Straight | Flush | FullHouse | FourOfAKind | StraightFlush | RoyalFlush deriving (Eq, Ord, Show)
67
What does show sc yield now?
535
Is this helping us solving our problem?
536
” (Just FullHouse,True)”.
Not totally. This would not print the score like we want it.
What is the value of showScore sc when sc = (Just Flush, True) ?
What is the value of showScore sc when sc = (Just Straight , False) ?
What is the value of showScore sc when sc = (Nothing, False) ?
Write the function showScore.
537
”Flush (winner)”.
538
” Straight ” .
539
” ” i.e. The empty String .
540
Here:
showScore :: Score → String showScore (Just c,True) = show c ++ ” (winner)” showScore (Just c,False) = show c showScore (Nothing, ) = ””
What is the value of showScore sc when sc = (Just ThreeOfAKind,False) ?
541
”ThreeOfAKind”.
Is the function showScore correct?
542
Not entirely.
What would be a better result?
543
”Three Of A Kind”.
544
”Three Of A Kind”.
545
”A Cat In The Hat”.
What is the value of the expression: unwords ws when ws = [”Three”,”Of”, ”A”,”Kind”] ?
What is the value of: unwords [”A”,”Cat”,”In” , ”The”,”Hat”] ?
What is the goal of the function: unwords [String] → String ?
unwords takes a list of String s and put them in a single String , separated by spaces. 546
Would unwords be useful for showing the Category value in English instead of Haskell?
547 Yes, provided we can convert the Category value into a list of String s.
How do we convert this value: FullHouse into this value: ”FullHouse” ?
We use show: show FullHouse == ”FullHouse”. 548
68
How do we convert this value: [ ” Full ” , ”House”] into this value: ” Full House” ?
How do we convert this value: ”FullHouse” into this value: [ ” Full ” , ”House”] ?
Let’s call it capWords.
What does the expression: capWords ”BusyAsABee” yield?
What does the expression:
549
We use unwords.
550
I don’t know. We need a new function.
551
Agreed.
552
[ ”Busy”,”As”,”A”,”Bee”] .
553
”Busy As A Bee” .
unwords (capwords ”BusyAsABee”)
yield?
What does the expression:
”Three Of A Kind”. This is what we need! 554
unwords (capWords (show ThreeOfAKind))
yield?
Can you write the function:
555
I don’t think I can.
556
Easy: each new word starts with a capital letter.
557
It’s the list:
capWords :: String → [ String ] ?
How do we delimit the words in the String : ”BusyAsABee” ?
What is the value of: map isUpper s when s = ”BusyAsABee”?
[True,False,False,False,True,False,True,True,False,False ]
Can you display the list on several lines in such way that each line start with the value True ?
558
Yes:
[True,False,False,False ,True,False ,True ,True,False,False]
How many lines are there?
559
4.
How do you characterize each line?
560
Do you remember about groups?
561
Yes.
What is the value of: groups ”hello bool”?
562
[ ”h”, ”e”, ” ll ” , ”o”, ” ” , ”b”, ”oo”,” l ” ] .
Each line starts with True, which can be followed by one or several False.
69
What is the value of group [4,8,0,0,7] ?
563
What is the value of groupBy (==) [4,8,0,0,7] ?
564
Same answer:
565
The same again:
What is the value of groupBy comp [4,8,0,0,7] when comp a x = x == a ?
What is the value of groupBy comp [4,8,0,7,6,5,0,2] when comp a x = even x == even a ?
[[4],[8],[0,0],[7]] .
[[4],[8],[0,0],[7]] .
[[4,8,0],[7],[6],[5],[0,2]]
566
Describe the function:
[[4],[8],[0,0],[7]] .
.
The function groupBy when given a comparison function and a list, groups elements of the list according to the comparison function. In each resulting group, applying the comparison function between the first member and each of the following members of the group should yield True. 567
groupBy :: (a → a → Bool) → [a] → [[ a ]]
in your own words.
568
comp a x = a == True && x == False
569
comp a x = a && not x
570
comp a x = a > x
Do you remember about comparing ?
571
Yes.
What is the value of:
572
”
573
comp a x = isUpper a && not (isUpper x)
Can you simplify the function?
574
comp a x = isUpper a > isUpper x
Can you simplify using comparing ?
575
comp a x = comparing isUpper a x == GT
Write the function capWords :: String → [ String ] .
576
OK:
How would you define comp so that groupBy comp [True,False,False,True,True,False] yields [[ True,False,False ],[ True ],[ True,False]] ?
Can you simplify this function?
Can you simplify this function? Remember that True > False.
:AaAabBccdDeennnnuu”
sortBy (comparing toUpper)”Abundance:A Bun Dance”
How would you define comp so that groupBy comp ”BusyAsABee” yields [ ”Busy”,”As”,”A”,”Bee”]?
capWords :: String → [ String ] capWords = groupBy comp where comp a x = comparing isUpper a x == GT
70
Write the function showCategory :: Category → String. Here are some tests it should pass:
577
OK:
showCategory :: Category → String showCategory = unwords ◦ capWords ◦ show where capWords :: String → [ String ] capWords = groupBy comp where comp f n = comparing isUpper f n == GT
showCategory ThreeOfAKind ==”Three Of A Kind” showCategory HighCard ==”High Card” showCategory Straight == ” Straight ”
Perfect! Now write the function showScore :: Score → String. It should pass the followng test:
578
I believe I can do it:
showScore :: Score → String showScore (Just c,True) = showCategory c ++” (winner)” showScore (Just c,False) = showCategory c showScore (Nothing, ) = ””
let sd = map cards ( [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q” , ”9♣ Ar K♠ Kq 9q 3♣ 6q” , ”A♣ Q♣ K♠ K♠ 9♠ 3♣” , ”9r 5♠” , ”4q 2q K♠ Kq 9q 3♣ 6q” , ”7♠ T♠ K♠ Kq 9q”]) sc = scores sd in (map showScore sc) == [ ” Full House (winner)” , ”Two Pairs” ,”” ,”” , ”Flush” ,””]
Now write the function
579
showDown :: String → Score → String.
that is easy also:
showDown :: String → Score → String showDown s (Nothing,False) = s showDown s sc = s ++ ’ ’ : showScore sc
It should passe the following test: showDown ”K♣ K♠ 7♠ Nq 9q 3♣ 6q” (Just Pair, True) == ”K♣ K♠ 7♠ Nq 9q 3♣ 6q Pair (winner)” showDown ”9♠ K♠” (Nothing, False) == ”9♠ K♠”
We are almost done.
What is the last thing we need?
580
Apply the function showDown to all the lines and
Scores in the game.
Write the function
581
showDowns :: [String] → [ String ] .
Ok. Here it is:
showDowns :: [String] → [ String ] showDowns ls = zipWith showDown ls sc where sc = scores (map cards ls)
It should pass this test: let ss = [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q” , ”9r 5♠” , ”4q 2q K♠ Kq 9q 3♣ 6q”] in showDowns ss == [ ”K♣ 9♠ K♠ Kq 9q 3♣ 6q Full House (winner)” , ”9r 5♠” , ”4q 2q K♠ Kq 9q 3♣ 6q Flush”]
71
Perfect! What is left to do?
582
Call our function showDowns from the main function of our program!
Let’s do this!
583
Alright!
main = interact (unlines ◦ showDowns ◦ lines)
Let’s try it. Here’s the content of the file Sample.txt: K♣ 9♣ A♣ 9r 4q 7♠
9♠ Ar Q♣ 5♠ 2q T♠
584
Let’s give it to our program:
runhaskell Scores