《Think Python 2e》学习精粹(九): 文字游戏
文章目录
1、读取单词列表
- Grady Ward 收集并贡献给公众的单词列表:words.txt;
- 内建函数 open 接受文件名作为形参,并返回一个 文件对象(file object) ,可以使用它读取该文件,文件名即 python 运行目录 (本文实验环境 python 运行目录为 C:\Users\Administrator)中文件的文件名;
- 该文件对象提供了几个读取方法, 包括 readline ,其从文件中读取字符直到碰到新行(即回车符),并将结果作为字符串返回;
>>> fin = open('words.txt')
>>> word = fin.readline()
>>> word
'aa\n'
- 此文件对象跟踪它在文件中的位置, 所以如果你再次调用readline,获得下一个单词 ;
- 可以用字符串方法 strip 删掉回车符 ‘\n’ ;
>>> word.strip()
'aa'
- 可以将文件对象用做for循环的一部分;
fin = open('words.txt')
for line in fin:
word = line.strip()
if len(word) >= 20:
print(word)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new42.py
counterdemonstration
counterdemonstrations
counterdemonstrators
hyperaggressivenesses
hypersensitivenesses
microminiaturization
microminiaturizations
representativenesses
2、练习
习题9-1:打印出长度超过20的单词
【习题 】 编程写一个程序,使得它可以读取 words.txt ,然后只打印出那些长度超过20个字符的单词(不包括空格)
- 练习记录:
fin = open('words.txt')
for line in fin:
word = line.strip()
if len(word) > 20:
print(word)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new42.py
counterdemonstrations
hyperaggressivenesses
microminiaturizations
习题9-2:寻找不包含字符“e”的单词
【习题 】 写一个叫做has_no_e的函数,如果给定的单词中不包含字符“e”,其返回 True ,修改上一节中的程序,只打印不包含“e”的单词,并且计算列表中不含“e”单词的比例
def has_no_e(word):
for letter in word:
if letter == 'e':
return False
return True
fin = open('words.txt')
m = 0
n = 0
for line in fin:
word = line.strip()
if len(word) > 20:
if has_no_e(word):
print(word)
n = n+1
m = m+1
per = n/m*100
print('per =',per)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new43.py
microminiaturizations
per = 33.33333333333333
习题9-3:寻找不包含字符串中字符的单词
【习题9.3 .1】 编写一个名为 avoids 的函数,接受一个单词和一个指定禁止使用字符的字符串, 如果单词中不包含任意被禁止的字符,则返回True ,修改你的程序,提示用户输入一个禁止使用的字符串,然后打印 words.txt 中不包含字符串中任一字符的单词的数量
- 练习记录:
def avoids(word, str):
for letter in word:
for line in str:
if letter == line:
return False
return True
fin = open('words.txt')
str = input('Please enter a string:')
x = 0
for line in fin:
word = line.strip()
if avoids(word, str):
x = x+1
print(x)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new46.py
Please enter a string: hello
9552
- 变形练习: 打印 words.txt 中不包含字母表各个字母的单词数量
def avoids(word, str):
for letter in word:
for line in str:
if letter == line:
return False
return True
for i in range(26):
str = chr(ord('a')+i)
fin = open('words.txt')
x = 0
for line in fin:
word = line.strip()
if avoids(word, str):
x = x+1
print(str,':', x)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new45.py
a : 57196
b : 97504
c : 83343
d : 83161
e : 37641
f : 102532
g : 88830
h : 94713
i : 53495
j : 112062
k : 104831
l : 73676
m : 91335
n : 64127
o : 69152
p : 90840
q : 112177
r : 58908
s : 49006
t : 66530
u : 84934
v : 104917
w : 105541
x : 111118
y : 100668
z : 110358
【习题9.3.2】 找到一个5个禁止使用字符的组合,使得其排除的单词数目最少
- 练习记录:
def avoids(word, str):
for letter in word:
for line in str:
if letter == line:
return False
return True
n = 0
string = ''
for i in range(0, 26):
for j in range(i+1, 26):
for k in range(j+1, 26):
for l in range(k+1, 26):
for m in range(l+1,26):
str = chr(ord('a')+i)+chr(ord('a')+j)+chr(ord('a')+k)+chr(ord('a')+l)+chr(ord('a')+m)
print(str, end = ' : ')
fin = open('words.txt')
x = 0
for line in fin:
word = line.strip()
if avoids(word, str):
x = x+1
print(x, end=' , ')
if x > n:
n = x
string = str
print(n)
print(string, n)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new44.py
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new44.py
abcde : 7990 , 7990
abcdf : 22286 , 22286
abcdg : 18914 , 22286
abcdh : 21124 , 22286
abcdi : 10857 , 22286
abcdj : 25371 , 25371
abcdk : 23694 , 25371
……
uvxyz : 63733 , 96425
uwxyz : 63553 , 96425
vwxyz : 80593 , 96425
jqwxz 96425
- 结果分析:由于本题练习所用算法是从字母表中的26个字母中取5个的组合组成一个字符串,共可以组合成 C526 = 65780 个不同的字符串,分别计算不含该字符串中字母的单词数量,然后找出数量最大的那个字符串,这个算法计算量太大,我把 shell 打开了第二天才看到结果:
jqwxz 96425
,不含 jqwxz 这个字符串中任一字母的单词最多(即排除最少),以后有更好的算法再改善,得到的结果其实从上题的变形练习就可以猜到
习题9-4:指定字母组词、造句
【习题】 编写一个名为uses_only的函数,接受一个单词和一个字符串。 如果该单词只包括此字符串中的字符,则返回True,你能只用 acefhlo 这几个字符造一个句子么? 除了“Hoe alfalfa”外
- 练习记录:
def uses_only(word, str):
for letter in word:
a = False
for line in str:
if letter == line:
a = True
if a == False:
return False
return True
fin = open('words.txt')
for line in fin:
word = line.strip()
if uses_only(word, 'acefhlo'):
print(word)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new50.py
aa
aah
aal
ace
ache
acho
……
olea
oleo
olla
ooh
- 结果分析:程序结果罗列了所有仅由 acefhlo 这些字符组成的单词,至于用这些单词造句么,嘿嘿,我英语不好,似乎其中没有一个单词能看懂,造句就算了吧
习题9-5:包含指定的所有字符的单词
【习题】 编写一个名为uses_all的函数,接受一个单词和一个必须使用的字符组成的字符串。 如果该单词包括此字符串中的全部字符至少一次,则返回True, 你能统计出多少单词包含了所有的元音字符 aeiouy 吗?
def uses_all(word, str):
for letter in str:
if letter not in word:
return False
return True
fin = open('words.txt')
n = 0
for line in fin:
word = line.strip()
if uses_all(word, 'aeiouy'):
n = n+1
print(word)
print(n)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new52.py
abstemiously
adventitiously
aeronautically
……
unintentionally
unquestionably
42
- 变形练习:
def uses_all(word, str):
for letter in str:
if letter in word:
continue
else:
return False
return True
- 结果分析:对比上一题的代码,可以发现用 in 和 not in 两个布尔运算符能减少许多代码,且更直观和便于理解
习题9-6:字母以字母表程序出现的单词
【习题】 编写一个名为is_abecedarian的函数, 如果单词中的字符以字符表的顺序出现(允许重复字符),则返回True , 有多少个具备这种特征的单词?
- 练习记录:
def is_abecedarian(word):
i = 1
for letter in word:
for j in range(i, len(word)):
if letter >= word[j]:
if j == i and letter == word[j]:
continue
return False
i = i+1
return True
fin = open('words.txt')
n = 0
for line in fin:
word = line.strip()
if is_abecedarian(word):
n = n+1
print(n, word)
PS C:\Users\Administrator> python D:\WorkSpace\thinkpython2e\new55.py
1 aa
2 aah
3 aahs
4 aal
5 aals
……
592 oy
593 pry
594 psst
595 sty
596 tux
- 结果分析:
if j == i and letter == word[j]:
语句的加入是为了允许有重复的字符出现
3、搜索
- avoids;
def avoids(word, forbidden):
for letter in word:
if letter in forbidden:
return False
return True
- uses_only;
def uses_only(word, available):
for letter in word:
if letter not in available:
return False
return True
- 简化为之前已解决的问题(reduction to a previously solved problem)的程序开发方法的一个示例,也就是说,认识到当前面临的问题是之前已经解决的问题的一个实例, 然后应用了已有的解决方案:uses_all;
def uses_all(word, required):
for letter in required:
if letter not in word:
return False
return True
def uses_all(word, required):
return uses_only(required, word)
4、使用索引进行循环
- for 循环写 is_abecedarian;
def is_abecedarian(word):
previous = word[0]
for c in word:
if c < previous:
return False
previous = c
return True
- 递归法写 is_abecedarian;
def is_abecedarian(word):
if len(word) <= 1:
return True
if word[0] > word[1]:
return False
return is_abecedarian(word[1:])
- while 循环写 is_abecedarian;
def is_abecedarian(word):
i = 0
while i < len(word)-1:
if word[i+1] < word[i]:
return False
i = i+1
return True
- is_palindrome 函数的一种版本;
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
- is_palindrome 函数的另一种版本;
def is_palindrome(word):
return is_reverse(word, word)
5、调试
- 选择一可以测试所有可能错误的单词集合,是很困难的,介于困难和不可能之间;
- 以 has_no_e为例,有两个明显的用例需要检查: 含有‘e’的单词应该返回 False ,不含的单词应该返回 True , 你应该可以很容易就能想到这两种情况;
- 在每个用例中,还有一些不那么明显的子用例, 在含有“e”的单词中,你应该测试“e”在开始、结尾以及在中间的单词,你还应该测试长单词、短单词以及非常短的单词,如空字符串,空字符串是一个特殊用例(special case),及一个经常出现错误的不易想到的用例;
- 除了自己生成的测试用例,也可以用一个类似 words.txt 中的单词列表测试你的程序, 通过扫描输出,可能会捕获错误,但是请注意: 可能捕获一类错误(包括了不应该包括的单词) 却没能捕获另一类错误(没有包括应该包括的单词);
- 一位传奇计算机科学家—Edsger W. Dijkstra 所说:程序测试能用于展示错误的存在,但是无法证明不存在错误!