Texas Hold'em (The Little Haskeller) [1 ed.]

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

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