-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththinkpython2010.html
More file actions
376 lines (350 loc) · 26.1 KB
/
thinkpython2010.html
File metadata and controls
376 lines (350 loc) · 26.1 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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<meta name="generator" content="hevea 2.09">
<link rel="stylesheet" type="text/css" href="thinkpython2.css">
<title>Case study: word play</title>
</head>
<body>
<a href="thinkpython2009.html"><img src="back.png" ALT="Previous"></a>
<a href="index.html.1"><img src="up.png" ALT="Up"></a>
<a href="thinkpython2011.html"><img src="next.png" ALT="Next"></a>
<hr>
<table>
<tr>
<td valign="top" width="100" bgcolor="#b6459a">
</td>
<td valign="top" width="600" style="padding: 20px 20px;">
<p>
<a href="http://amzn.to/1VUYQUU">Buy this book at Amazon.com</a>
<h1 class="chapter" id="sec105">Chapter 9  Case study: word play</h1>
<p>
<a id="wordplay"></a></p><p>This chapter presents the second case study, which involves
solving word puzzles by searching for words that have certain
properties. For example, we’ll find the longest palindromes
in English and search for words whose letters appear in
alphabetical order. And I will present another program development
plan: reduction to a previously solved problem.</p>
<h2 class="section" id="sec106">9.1  Reading word lists</h2>
<p>
<a id="wordlist"></a></p><p>For the exercises in this chapter we need a list of English words.
There are lots of word lists available on the Web, but the one most
suitable for our purpose is one of the word lists collected and
contributed to the public domain by Grady Ward as part of the Moby
lexicon project (see <a href="http://wikipedia.org/wiki/Moby_Project"><span class="c004">http://wikipedia.org/wiki/Moby_Project</span></a>). It
is a list of 113,809 official crosswords; that is, words that are
considered valid in crossword puzzles and other word games. In the
Moby collection, the filename is <span class="c004">113809of.fic</span>; you can download
a copy, with the simpler name <span class="c004">words.txt</span>, from
<a href="http://thinkpython2.com/code/words.txt"><span class="c004">http://thinkpython2.com/code/words.txt</span></a>.
<a id="hevea_default644"></a>
<a id="hevea_default645"></a></p><p>This file is in plain text, so you can open it with a text
editor, but you can also read it from Python. The built-in
function <span class="c004">open</span> takes the name of the file as a parameter
and returns a <span class="c010">file object</span> you can use to read the file.
<a id="hevea_default646"></a>
<a id="hevea_default647"></a>
<a id="hevea_default648"></a>
<a id="hevea_default649"></a>
<a id="hevea_default650"></a>
<a id="hevea_default651"></a></p><pre class="verbatim">>>> fin = open('words.txt')
</pre><p><span class="c004">fin</span> is a common name for a file object used for input. The file
object provides several methods for reading, including <span class="c004">readline</span>,
which reads characters from the file until it gets to a newline and
returns the result as a string: <a id="hevea_default652"></a>
<a id="hevea_default653"></a></p><pre class="verbatim">>>> fin.readline()
'aa\r\n'
</pre><p>The first word in this particular list is “aa”, which is a kind of
lava. The sequence <code>\r\n</code> represents two whitespace characters,
a carriage return and a newline, that separate this word from the
next.</p><p>The file object keeps track of where it is in the file, so
if you call <span class="c004">readline</span> again, you get the next word:</p><pre class="verbatim">>>> fin.readline()
'aah\r\n'
</pre><p>The next word is “aah”, which is a perfectly legitimate
word, so stop looking at me like that.
Or, if it’s the whitespace that’s bothering you,
we can get rid of it with the string method <span class="c004">strip</span>:
<a id="hevea_default654"></a>
<a id="hevea_default655"></a></p><pre class="verbatim">>>> line = fin.readline()
>>> word = line.strip()
>>> word
'aahed'
</pre><p>You can also use a file object as part of a <span class="c004">for</span> loop.
This program reads <span class="c004">words.txt</span> and prints each word, one
per line:
<a id="hevea_default656"></a>
<a id="hevea_default657"></a></p><pre class="verbatim">fin = open('words.txt')
for line in fin:
word = line.strip()
print(word)
</pre>
<h2 class="section" id="sec107">9.2  Exercises</h2>
<p>There are solutions to these exercises in the next section.
You should at least attempt each one before you read the solutions.</p><div class="theorem"><span class="c010">Exercise 1</span>  <em>
Write a program that reads <span class="c004">words.txt</span> and prints only the
words with more than 20 characters (not counting whitespace).
</em><a id="hevea_default658"></a></div><div class="theorem"><span class="c010">Exercise 2</span>  <p><em>In 1939 Ernest Vincent Wright published a 50,000 word novel called
</em>Gadsby<em> that does not contain the letter “e”. Since “e” is
the most common letter in English, that’s not easy to do.</em></p><p><em>In fact, it is difficult to construct a solitary thought without using
that most common symbol. It is slow going at first, but with caution
and hours of training you can gradually gain facility.</em></p><p><em>All right, I’ll stop now.</em></p><p><em>Write a function called <code>has_no_e</code> that returns <span class="c004">True</span> if
the given word doesn’t have the letter “e” in it.</em></p><p><em>Modify your program from the previous section to print only the words
that have no “e” and compute the percentage of the words in the list
that have no “e”.
</em><a id="hevea_default659"></a></p></div><div class="theorem"><span class="c010">Exercise 3</span>   <p><em>Write a function named <span class="c004">avoids</span>
that takes a word and a string of forbidden letters, and
that returns <span class="c004">True</span> if the word doesn’t use any of the forbidden
letters.</em></p><p><em>Modify your program to prompt the user to enter a string
of forbidden letters and then print the number of words that
don’t contain any of them.
Can you find a combination of 5 forbidden letters that
excludes the smallest number of words?</em></p></div><div class="theorem"><span class="c010">Exercise 4</span>  <p><em>Write a function named <code>uses_only</code> that takes a word and a
string of letters, and that returns <span class="c004">True</span> if the word contains
only letters in the list. Can you make a sentence using only the
letters <span class="c004">acefhlo</span>? Other than “Hoe alfalfa?”</em></p></div><div class="theorem"><span class="c010">Exercise 5</span>   <p><em>Write a function named <code>uses_all</code> that takes a word and a
string of required letters, and that returns <span class="c004">True</span> if the word
uses all the required letters at least once. How many words are there
that use all the vowels <span class="c004">aeiou</span>? How about <span class="c004">aeiouy</span>?</em></p></div><div class="theorem"><span class="c010">Exercise 6</span>  <p><em>Write a function called <code>is_abecedarian</code> that returns
<span class="c004">True</span> if the letters in a word appear in alphabetical order
(double letters are ok).
How many abecedarian words are there?</em></p><p><a id="hevea_default660"></a></p></div>
<h2 class="section" id="sec108">9.3  Search</h2>
<p>
<a id="search"></a>
<a id="hevea_default661"></a>
<a id="hevea_default662"></a></p><p>All of the exercises in the previous section have something
in common; they can be solved with the search pattern we saw
in Section <a href="thinkpython2009.html#find">8.6</a>. The simplest example is:</p><pre class="verbatim">def has_no_e(word):
for letter in word:
if letter == 'e':
return False
return True
</pre><p>The <span class="c004">for</span> loop traverses the characters in <span class="c004">word</span>. If we find
the letter “e”, we can immediately return <span class="c004">False</span>; otherwise we
have to go to the next letter. If we exit the loop normally, that
means we didn’t find an “e”, so we return <span class="c004">True</span>.
<a id="hevea_default663"></a></p><p><a id="hevea_default664"></a>
<a id="hevea_default665"></a>
You could write this function more concisely using the <span class="c004">in</span>
operator, but I started with this version because it
demonstrates the logic of the search pattern.</p><p><a id="hevea_default666"></a>
<span class="c004">avoids</span> is a more general version of <code>has_no_e</code> but it
has the same structure:</p><pre class="verbatim">def avoids(word, forbidden):
for letter in word:
if letter in forbidden:
return False
return True
</pre><p>We can return <span class="c004">False</span> as soon as we find a forbidden letter;
if we get to the end of the loop, we return <span class="c004">True</span>.</p><p><code>uses_only</code> is similar except that the sense of the condition
is reversed:</p><pre class="verbatim">def uses_only(word, available):
for letter in word:
if letter not in available:
return False
return True
</pre><p>Instead of a list of forbidden letters, we have a list of available
letters. If we find a letter in <span class="c004">word</span> that is not in
<span class="c004">available</span>, we can return <span class="c004">False</span>.</p><p><code>uses_all</code> is similar except that we reverse the role
of the word and the string of letters:</p><pre class="verbatim">def uses_all(word, required):
for letter in required:
if letter not in word:
return False
return True
</pre><p>Instead of traversing the letters in <span class="c004">word</span>, the loop
traverses the required letters. If any of the required letters
do not appear in the word, we can return <span class="c004">False</span>.
<a id="hevea_default667"></a></p><p>If you were really thinking like a computer scientist, you would
have recognized that <code>uses_all</code> was an instance of a
previously solved problem, and you would have written:</p><pre class="verbatim">def uses_all(word, required):
return uses_only(required, word)
</pre><p>This is an example of a program development plan called <span class="c010">reduction to a previously solved problem</span>, which means that you
recognize the problem you are working on as an instance of a solved
problem and apply an existing solution. <a id="hevea_default668"></a> <a id="hevea_default669"></a></p>
<h2 class="section" id="sec109">9.4  Looping with indices</h2>
<p>
<a id="hevea_default670"></a>
<a id="hevea_default671"></a></p><p>I wrote the functions in the previous section with <span class="c004">for</span>
loops because I only needed the characters in the strings; I didn’t
have to do anything with the indices.</p><p>For <code>is_abecedarian</code> we have to compare adjacent letters,
which is a little tricky with a <span class="c004">for</span> loop:</p><pre class="verbatim">def is_abecedarian(word):
previous = word[0]
for c in word:
if c < previous:
return False
previous = c
return True
</pre><p>An alternative is to use recursion:</p><pre class="verbatim">def is_abecedarian(word):
if len(word) <= 1:
return True
if word[0] > word[1]:
return False
return is_abecedarian(word[1:])
</pre><p>Another option is to use a <span class="c004">while</span> loop:</p><pre class="verbatim">def is_abecedarian(word):
i = 0
while i < len(word)-1:
if word[i+1] < word[i]:
return False
i = i+1
return True
</pre><p>The loop starts at <span class="c004">i=0</span> and ends when <span class="c004">i=len(word)-1</span>. Each
time through the loop, it compares the <span class="c009">i</span>th character (which you can
think of as the current character) to the <span class="c009">i</span>+1th character (which you
can think of as the next).</p><p>If the next character is less than (alphabetically before) the current
one, then we have discovered a break in the abecedarian trend, and
we return <span class="c004">False</span>.</p><p>If we get to the end of the loop without finding a fault, then the
word passes the test. To convince yourself that the loop ends
correctly, consider an example like <code>'flossy'</code>. The
length of the word is 6, so
the last time the loop runs is when <span class="c004">i</span> is 4, which is the
index of the second-to-last character. On the last iteration,
it compares the second-to-last character to the last, which is
what we want.
<a id="hevea_default672"></a></p><p>Here is a version of <code>is_palindrome</code> (see
Exercise <a href="thinkpython2007.html#palindrome">3</a>) that uses two indices; one starts at the
beginning and goes up; the other starts at the end and goes down.</p><pre class="verbatim">def is_palindrome(word):
i = 0
j = len(word)-1
while i<j:
if word[i] != word[j]:
return False
i = i+1
j = j-1
return True
</pre><p>Or we could reduce to a previously solved
problem and write:
<a id="hevea_default673"></a>
<a id="hevea_default674"></a></p><pre class="verbatim">def is_palindrome(word):
return is_reverse(word, word)
</pre><p>Using <code>is_reverse</code> from Section <a href="thinkpython2009.html#isreverse">8.11</a>.</p>
<h2 class="section" id="sec110">9.5  Debugging</h2>
<p>
<a id="hevea_default675"></a>
<a id="hevea_default676"></a>
<a id="hevea_default677"></a></p><p>Testing programs is hard. The functions in this chapter are
relatively easy to test because you can check the results by hand.
Even so, it is somewhere between difficult and impossible to choose a
set of words that test for all possible errors.</p><p>Taking <code>has_no_e</code> as an example, there are two obvious
cases to check: words that have an ‘e’ should return <span class="c004">False</span>, and
words that don’t should return <span class="c004">True</span>. You should have no
trouble coming up with one of each.</p><p>Within each case, there are some less obvious subcases. Among the
words that have an “e”, you should test words with an “e” at the
beginning, the end, and somewhere in the middle. You should test long
words, short words, and very short words, like the empty string. The
empty string is an example of a <span class="c010">special case</span>, which is one of
the non-obvious cases where errors often lurk.
<a id="hevea_default678"></a></p><p>In addition to the test cases you generate, you can also test
your program with a word list like <span class="c004">words.txt</span>. By scanning
the output, you might be able to catch errors, but be careful:
you might catch one kind of error (words that should not be
included, but are) and not another (words that should be included,
but aren’t).</p><p>In general, testing can help you find bugs, but it is not easy to
generate a good set of test cases, and even if you do, you can’t
be sure your program is correct.
According to a legendary computer scientist:
<a id="hevea_default679"></a></p><blockquote class="quote">
Program testing can be used to show the presence of bugs, but never to
show their absence!<p>— Edsger W. Dijkstra
</p></blockquote><p>
<a id="hevea_default680"></a></p>
<h2 class="section" id="sec111">9.6  Glossary</h2>
<dl class="description"><dt class="dt-description"><span class="c010">file object:</span></dt><dd class="dd-description"> A value that represents an open file.
<a id="hevea_default681"></a>
<a id="hevea_default682"></a></dd><dt class="dt-description"><span class="c010">reduction to a previously solved problem:</span></dt><dd class="dd-description"> A way of solving a
problem by expressing it as an instance of a previously solved
problem. <a id="hevea_default683"></a>
<a id="hevea_default684"></a></dd><dt class="dt-description"><span class="c010">special case:</span></dt><dd class="dd-description"> A test case that is atypical or non-obvious
(and less likely to be handled correctly).
<a id="hevea_default685"></a></dd></dl>
<h2 class="section" id="sec112">9.7  Exercises</h2>
<div class="theorem"><span class="c010">Exercise 7</span>  
<a id="hevea_default686"></a>
<a id="hevea_default687"></a>
<a id="hevea_default688"></a><p><em>This question is based on a Puzzler that was broadcast on the radio
program </em>Car Talk<em>
(</em><a href="http://www.cartalk.com/content/puzzlers"><em><span class="c004">http://www.cartalk.com/content/puzzlers</span></em></a><em>):</em></p><blockquote class="quote"><em>
Give me a word with three consecutive double letters. I’ll give you a
couple of words that almost qualify, but don’t. For example, the word
committee, c-o-m-m-i-t-t-e-e. It would be great except for the ‘i’ that
sneaks in there. Or Mississippi: M-i-s-s-i-s-s-i-p-p-i. If you could
take out those i’s it would work. But there is a word that has three
consecutive pairs of letters and to the best of my knowledge this may
be the only word. Of course there are probably 500 more but I can only
think of one. What is the word?
</em></blockquote><p><em>Write a program to find it.
Solution: </em><a href="http://thinkpython2.com/code/cartalk1.py"><em><span class="c004">http://thinkpython2.com/code/cartalk1.py</span></em></a><em>.</em></p></div><div class="theorem"><span class="c010">Exercise 8</span>  <em>
Here’s another </em>Car Talk<em>
Puzzler (</em><a href="http://www.cartalk.com/content/puzzlers"><span class="c004"><em>http://www.cartalk.com/content/puzzlers</em></span></a><em>):
</em><a id="hevea_default689"></a>
<a id="hevea_default690"></a>
<a id="hevea_default691"></a>
<a id="hevea_default692"></a><blockquote class="quote"><em>
“I was driving on the highway the other day and I happened to
notice my odometer. Like most odometers, it shows six digits,
in whole miles only. So, if my car had 300,000
miles, for example, I’d see 3-0-0-0-0-0.</em><p><em>“Now, what I saw that day was very interesting. I noticed that the
last 4 digits were palindromic; that is, they read the same forward as
backward. For example, 5-4-4-5 is a palindrome, so my odometer
could have read 3-1-5-4-4-5.</em></p><p><em>“One mile later, the last 5 numbers were palindromic. For example, it
could have read 3-6-5-4-5-6. One mile after that, the middle 4 out of
6 numbers were palindromic. And you ready for this? One mile later,
all 6 were palindromic!</em></p><p><em>“The question is, what was on the odometer when I first looked?”
</em></p></blockquote><p><em>Write a Python program that tests all the six-digit numbers and prints
any numbers that satisfy these requirements.
Solution: </em><a href="http://thinkpython2.com/code/cartalk2.py"><em><span class="c004">http://thinkpython2.com/code/cartalk2.py</span></em></a><em>.</em></p></div><div class="theorem"><span class="c010">Exercise 9</span>  <em>
Here’s another </em>Car Talk<em> Puzzler you can solve with a
search (</em><a href="http://www.cartalk.com/content/puzzlers"><span class="c004"><em>http://www.cartalk.com/content/puzzlers</em></span></a><em>):
</em><a id="hevea_default693"></a>
<a id="hevea_default694"></a>
<a id="hevea_default695"></a><blockquote class="quote"><em>
“Recently I had a visit with my mom and we realized that
the two digits that make up my age when reversed resulted in her
age. For example, if she’s 73, I’m 37. We wondered how often this has
happened over the years but we got sidetracked with other topics and
we never came up with an answer.</em><p><em>“When I got home I figured out that the digits of our ages have been
reversible six times so far. I also figured out that if we’re lucky it
would happen again in a few years, and if we’re really lucky it would
happen one more time after that. In other words, it would have
happened 8 times over all. So the question is, how old am I now?”</em></p></blockquote><p><em>Write a Python program that searches for solutions to this Puzzler.
Hint: you might find the string method <span class="c004">zfill</span> useful.</em></p><p><em>Solution: </em><a href="http://thinkpython2.com/code/cartalk3.py"><em><span class="c004">http://thinkpython2.com/code/cartalk3.py</span></em></a><em>.</em></p></div>
<p>
<a href="http://amzn.to/1VUYQUU">Buy this book at Amazon.com</a>
</td>
<td width=130 valign="top">
<p>
<h4>Are you using one of our books in a class?</h4> We'd like to know
about it. Please consider filling out <a href="http://spreadsheets.google.com/viewform?formkey=dC0tNUZkMjBEdXVoRGljNm9FRmlTMHc6MA" onClick="javascript: pageTracker._trackPageview('/outbound/survey');">this short survey</a>.
<p>
<br>
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491938455/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491938455&linkCode=as2&tag=greenteapre01-20&linkId=2JJH4SWCAVVYSQHO">Think DSP</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491938455" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491938455/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491938455&linkCode=as2&tag=greenteapre01-20&linkId=CTV7PDT7E5EGGJUM"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491938455&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491938455" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491929561/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491929561&linkCode=as2&tag=greenteapre01-20&linkId=ZY6MAYM33ZTNSCNZ">Think Java</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491929561" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491929561/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491929561&linkCode=as2&tag=greenteapre01-20&linkId=PT77ANWARUNNU3UK"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491929561&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491929561" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449370780/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1449370780&linkCode=as2&tag=greenteapre01-20">Think Bayes</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449370780" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449370780/ref=as_li_qf_sp_asin_il?ie=UTF8&camp=1789&creative=9325&creativeASIN=1449370780&linkCode=as2&tag=greenteapre01-20"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1449370780&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449370780" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491939362/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491939362&linkCode=as2&tag=greenteapre01-20&linkId=FJKSQ3IHEMY2F2VA">Think Python 2e</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491939362" width="1" height="1" border="0" alt="">
<p>
<a rel="nofollow" href="http://www.amazon.com/gp/product/1491939362/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491939362&linkCode=as2&tag=greenteapre01-20&linkId=ZZ454DLQ3IXDHNHX"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491939362&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491939362" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1491907339/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491907339&linkCode=as2&tag=greenteapre01-20&linkId=O7WYM6H6YBYUFNWU">Think Stats 2e</a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491907339" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1491907339/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1491907339&linkCode=as2&tag=greenteapre01-20&linkId=JVSYKQHYSUIEYRHL"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1491907339&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://ir-na.amazon-adsystem.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1491907339" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449314635/ref=as_li_tf_tl?ie=UTF8&tag=greenteapre01-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=1449314635">Think Complexity</a><img class="c003" src="http://www.assoc-amazon.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449314635" width="1" height="1" border="0" alt="">
<p>
<a href="http://www.amazon.com/gp/product/1449314635/ref=as_li_tf_il?ie=UTF8&camp=1789&creative=9325&creativeASIN=1449314635&linkCode=as2&tag=greenteapre01-20"><img border="0" src="http://ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1449314635&Format=_SL160_&ID=AsinImage&MarketPlace=US&ServiceVersion=20070822&WS=1&tag=greenteapre01-20"></a><img class="c003" src="http://www.assoc-amazon.com/e/ir?t=greenteapre01-20&l=as2&o=1&a=1449314635" width="1" height="1" border="0" alt="">
</td>
</tr>
</table>
<hr>
<a href="thinkpython2009.html"><img src="back.png" ALT="Previous"></a>
<a href="index.html.1"><img src="up.png" ALT="Up"></a>
<a href="thinkpython2011.html"><img src="next.png" ALT="Next"></a>
</body>
</html>