-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathChapter3.tex
More file actions
182 lines (174 loc) · 6.01 KB
/
Chapter3.tex
File metadata and controls
182 lines (174 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
\newpage
%----------------------------------------------------------------------------------------------------
\section{Looking for a Flush}
%----------------------------------------------------------------------------------------------------
\vspace{10cm}
\hrule
\lhQ What is the next task with regard to card comparison?
\lhA We need to compare suits so that we can find a \emph{flush}.
\lhN Ok I'll write a test:
\begin{lstlisting}[frame=single]
ud = words "A♣ 2♣ T♣ K♣ 9♣ Q♣ J♣"
sd = words "2♣ 9♣ T♣ J♣ Q♣ K♣ A♣"
main = runTestTT $ TestList
[sortBy (comparing card) ud ~?= sd
,flush (cards "A♣ T♣ 3♣ 4♣ 2♣") ~?= True]
where cards = map card . words
\end{lstlisting} % $
\hspace*{\fill}
\lhA \error Let's write a function \il!flush!
\begin{lstlisting}[frame=single]
flush :: [Card] -> Bool
flush _ = True
\end{lstlisting}
\success Done.
\lhN I see. Still the \emph{fake it 'til you make it} approach.
\lhA This is the simplest thing that makes the test pass.
\lhN Ok. Here is another test:
\begin{lstlisting}[frame=single]
main = runTestTT $ TestList
[sortBy (comparing card) ud ~?= sd
,flush (cards "A♣ T♣ 3♣ 4♣ 2♣") ~?= True
,flush (cards "A♠ T♣ 3♣ 4♣ 2♣") ~?= False ]
where cards = map card . words
\end{lstlisting} % $
Can you make it pass?
\lhA \failure I don't think so.
\lhN What is missing?
\lhA The \il!Card! type doesn't include suits.
\lhN How can we change that?
\lhA Add a failing test on getting \il!Suit!s from \il!Card!s.
\lhN Ok, then I'll replace my last test with this one:
\begin{lstlisting}[frame=single]
main = runTestTT $ TestList
[sortBy (comparing card) ud ~?= sd
,flush (cards "A♣ T♣ 3♣ 4♣ 2♣") ~?= True
,map suit (cards "A♣ A♦ A♣ A♠") ~?= ['♣','♦','♣','♠']]
where cards = map card . words
\end{lstlisting} % $
Can you make this one pass?
\lhA \error First we need a \il!suit! function:
\begin{lstlisting}[frame=single]
type Suit = Char
suit :: Card -> Suit
suit _ = '♣'
\end{lstlisting}
\failure Now the test is failing.
\lhN What else is needed?
\lhA \failure We must store the suit into to the \il!Card! type:
\begin{lstlisting}[frame=single]
data Card = C Value Suit deriving (Ord,Eq)
\end{lstlisting}
And then we have to capture the suit in the \il!card! function:
\begin{lstlisting}[frame=single]
card :: String -> Card
card ['A',s] = C 14 s
card ['K',s] = C 13 s
card ['Q',s] = C 12 s
card ['J',s] = C 11 s
card ['T',s] = C 10 s
card [c,s] = C ((ord c) - (ord '0')) s
\end{lstlisting}
\lhN \failure The code in the \il!card! function is a bit tedious, don't you think?
\lhA \failure I'll refactor it when the bar is green. I still have to remove the \emph{fake} on \il!suit!:
\begin{lstlisting}[frame=single]
suit :: Card -> Suit
suit (C _ s) = s
\end{lstlisting}
\success And now we can get \il!Suit!s from \il!Card!s.
\lhN Good. Refactor the code, now.
\lhA \success Allright. First I can discard the \il!suit! function by declaring labels:
\begin{lstlisting}[frame=single]
data Card = C { value :: Value, suit :: Suit }
deriving (Ord,Eq)
\end{lstlisting}
Then I can separate concerns in the \il!card! function:
\begin{lstlisting}[frame=single]
card :: String -> Card
card [v,s] = C (toValue v) s
where
toValue 'A' = 14
toValue 'K' = 13
toValue 'Q' = 12
toValue 'J' = 11
toValue 'T' = 10
toValue c = ((ord c) - (ord '0'))
\end{lstlisting}
\success Done.
\lhN Can I add my test on \il!flush! now?
\lhA Yes.
\newpage
\lhN Here it is:
\begin{lstlisting}[frame=single]
main = runTestTT $ TestList
[sortBy (comparing card) ud ~?= sd
,flush (cards "A♣ T♣ 3♣ 4♣ 2♣") ~?= True
,map suit (cards "A♣ A♦ A♣ A♠") ~?= ['♣','♦','♣','♠']
,flush (cards "A♠ T♣ 3♣ 4♣ 2♣") ~?= False]
where cards = map card . words
\end{lstlisting} % $
Do you see how to make it pass?
\lhA \failure Sure:
\begin{lstlisting}[frame=single]
flush :: [Card] -> Bool
flush (c:_) = suit c == '♣'
\end{lstlisting}
\success As you see, it's a \emph{fake}.
\lhN In that case, I'll add a new test :
\begin{lstlisting}[frame=single]
main = runTestTT $ TestList
[sortBy (comparing card) ud ~?= sd
,flush (cards "A♣ T♣ 3♣ 4♣ 2♣") ~?= True
,map suit (cards "A♣ A♦ A♥ A♠") ~?= ['♣','♦','♣','♠']
,flush (cards "A♠ T♣ 3♣ 4♣ 2♣") ~?= False
,flush (cards "A♠ T♠ 3♠ 4♠ 2♠") ~?= True]
where cards = map card . words
\end{lstlisting} % $
\lhA \failure Ok. I think I can take a more general approach:
\begin{lstlisting}[frame=single]
flush :: [Card] -> Bool
flush (c:cs) = all (\x -> suit x == suit c) cs
\end{lstlisting}
\success Of course, we're assuming that the \il!flush! function will always consume non-empty lists.
\lhN Ok. These are the tests so far:
\begin{lstlisting}[frame=single]
module Tests
where
import Test.HUnit
import PokerHand
import Data.Ord (comparing)
import Data.List (sort,sortBy)
ud = words "A♣ 2♣ T♣ K♣ 9♣ Q♣ J♣"
sd = words "2♣ 9♣ T♣ J♣ Q♣ K♣ A♣"
main = runTestTT $ TestList
[sortBy (comparing card) ud ~?= sd
,map suit (cards "A♣ A♦ A♥ A♠") ~?= ['♣','♦','♥','♠']
,flush (cards "A♣ T♣ 3♣ 4♣ 2♣") ~?= True
,flush (cards "A♠ T♣ 3♣ 4♣ 2♣") ~?= False
,flush (cards "A♠ T♠ 3♠ 4♠ 2♠") ~?= True]
where cards = map card . words
\end{lstlisting} % $
\lhA And this is the tested code:
\begin{lstlisting}[frame=single]
module PokerHand
where
import Char
data Card = C { value :: Value, suit :: Suit }
deriving (Ord,Eq)
type Value = Int
type Suit = Char
card :: String -> Card
card [v,s] = C (toValue v) s
where
toValue 'A' = 14
toValue 'K' = 13
toValue 'Q' = 12
toValue 'J' = 11
toValue 'T' = 10
toValue c = ((ord c) - (ord '0'))
flush :: [Card] -> Bool
flush (c:cs) = all (\x -> suit x == suit c) cs
\end{lstlisting}
\lhN Are we done with comparing cards?
\lhA I think so. Let's have lunch.
\lhend