10.3 Case Study: Lotto Numbers
LISTING 10.2 LottoNumbers.py
isCovered = 99 * [False] # 初始化一个列表
endOfInput = True #设定一个跳出loop的判定值
while endOfInput:
s = input("Enter a line of numbers separated by space:\n") #用户输入数字并以“空格”隔开
item = s.split()
lst = [eval(x) for x in item] # 注意这种语法格式convert every string in item to number
for number in lst:
if number == 0:
endOfInput = False
else:
isCovered[number -1] = True #将与之对应的位置标记成"True"
allCovered = True #初始化一个值,假设所有值都覆盖了
for i in range(99):
if isCovered[i] == False: #如果有数没有覆盖,初始值设为F
allCovered = False
break
if allCovered:
print("The tickets cover all numbers")
else:
print("The tickets don't cover all numbers")
----------------------------小节分割线-----------------------------
10.4 Case Study: Deck of Cards
从52张扑克牌中任意抽取4张。
deck = [x for x in range(52)]
初始化deck列表,并赋值0~51,代表52张牌。注意每个数字代表特定的扑克牌,后面会有解释为什么。(利用[ ]和for语句初始化列表),或
deck = list(range(52))
使用list()函数和range()函数初始化列表
数字 0~12 spades 黑桃,
数字 13~25 hearts 红心,
数字 26~38 diamands 方块,
数字 39~51 clubs 草花,
52张牌的号码对应52张扑克,例如: 牌号为 3 ,3 // 3 是 0 代表 Spades(黑桃),3 % 13 是 3 代表4,所以牌号3代表黑桃4
LISTING 10.3 DeckOfCards.py
deck = [x for x in range(52)]
suits = ["Spades", "Hearts", "Diamonds", "Clubs"]
ranks = ["Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"]
import random
random.shuffle(deck) #洗牌
for i in range(4):
suit = suits[deck[i] // 13]
rank = ranks[deck[i] % 13]
print("Card number", deck[i], "is the", rank, "of", suit)
-------------------------------小节分割线----------------------------------------------
10.5 Deck of Cards GUI
跳过
----------------------------------小节分割线-------------------------------------------
10.6 Copying Lists
list2 = list1
并不是把list1的值拷贝给list2,原因看下图
只是把list1的reference value传递给了list2
>>> list1 = [1, 2]
>>> list2 = [3, 4, 5]
>>> id(list1)
36207312
>>> id(list2)
36249848
>>>
>>> list2 = list1
>>> id(list2)
36207312
>>>
真正的复制为如下:
list2 = [x for x in list[1]]
or
list2 = [] + list1
问答题:
10.15 What is the output of the following code?
list1 = list(range(1, 10, 2))
list2 = list1
list1[0] = 111
print(list1)
print(list2)
>>>
[111, 3, 5, 7, 9]
[111, 3, 5, 7, 9]
>>>
10.16 What is the output of the following code?
list1 = list(range(1, 10, 2))
list2 = [] + list1
list1[0] = 111
print(list1)
print(list2)
>>>
[111, 3, 5, 7, 9]
[1, 3, 5, 7, 9]
---------------------------------小节分割线------------------------------------------------
10.7 Passing Lists to Functions
list(列表数据类型)也能作为一个参数输入到function(函数)中
LISTING 10.5 PassListArgument.py
def main():
x = 1 # x is an int variable
y = [1, 2, 3] # y is a list
m(x, y) # Invoke m with arguments x and y
print("x is", x)
print("y[0] is", y[0])
def m(number, numbers):
number = 1001 # Assign a new value to number
numbers[0] = 5555 # Assign a new value to numbers[0]
main() # Call the main function
结果如下:
x is 1
y[0] is 5555
可以看到函数m(number, numbers)被调用后,number是不可变数据类型,函数中创建了一个instance,函数结束后。函数外的original instance不变,所以x 任然是1,。但y和numbers都指向同一个list object,所以当number[0]的值被改变了,y[0]指向的值也同时变成了5555。
LISTING 10.6 DefaultListArgument.py
def add(x, lst = []):
if x not in lst:
lst.append(x)
return lst
def main():
list1 = add(1)
print(list1)
list2 = add(2)
print(list2)
list3 = add(3, [11, 12, 13, 14])
print(list3)
list4 = add(4)
print(list4)
main()
输出结果如下
[1]
[1, 2]
[11, 12, 13, 14, 3]
[1, 2, 4]
add()函数在第一次被调用时,创建了lst[],之后再次被调用时,如果不指明新的列表,会默认把数添加到之前创建的lst[]中。
如果想每次添加一个数字到一个空的列表中。可以这样:
LISTING 10.7 DefaultNoneListArgument.py
def add(x, lst = None): # 指定函数的第二个参数默认为None
if lst == None: # 如果没有给第二个参数,就创建一个[](空列表)
lst = []
if x not in lst:
lst.append(x)
return lst
def main():
list1 = add(1)
print(list1)
list2 = add(2)
print(list2)
list3 = add(3, [11, 12, 13, 14])
print(list3)
list4 = add(4)
print(list4)
main()
运行结果如下:
[1]
[2]
[11, 12, 13, 14, 3]
[4]
如此设置函数后,调用函数时,如果lst参数不给,就创建一个空列表。
-------------------------------------小节分割线------------------------------------------------------------
10.8 Returning a List from a Function
def reverse(lst):
result = [] # 创建一个新的列表,并初初始化
for element in lst:
result.insert(0, element) # 将列表中的元素逐一添加到新列表的第一位
return result
当然list class(列表类型)的数据自带 reverse()methed
问答题:
10.17 True or false? When a list is passed to a function, a new list is created and passed to
the function.
False . Listing 10.6已经详细说明。
10.18 Show the output of the following two programs:
def main():
number = 0
numbers = [10]
m(number, numbers)
print("number is", number, "and numbers[0] is", numbers[0])
def m(x, y):
x = 3
y[0] = 3
main()
number is 0 and numbers[0] is 3
def main():
lst = [1, 2, 3, 4, 5]
reverse(lst)
for value in lst:
print(value, end = ' ')
def reverse(lst):
newLst = len(lst) * [0]
for i in range(len(lst)):
newLst[i] = lst[len(lst) - 1 - i]
lst = newLst
main()
1 2 3 4 5
这题关键是reverse函数并没能将列表逆序排列
10.19 Show the output of the following two programs:
def main():
list1 = m(1)
print(list1)
list2 = m(1)
print(list2)
def m(x, lst = [1, 1, 2, 3]):
if x in lst:
lst.remove(x)
return lst
main()
[1, 2, 3]
[2, 3]
def main():
list1 = m(1)
print(list1)
list2 = m(1)
print(list2)
def m(x, lst = None):
if lst == None:
lst = [1, 1, 2, 3]
if x in lst:
lst.remove(x)
return lst
main()
[1, 2, 3]
[1, 2, 3]
-------------------------------------小节分割线----------------------------------------------
10.9 Case Study: Counting the Occurrences of Each Letter
1.(a)为chars列表,利用导入之前第六章用过的函数随机产生100个小写字母存入列表
2.(b)建立counts列表用于计数
LISTING 10.8 CountLettersInList.py
import RandomCharacter
def main():
chars = createList()
print("The lowercase letters are:")
displayList(chars)
counts = countLetters(chars)
print("The occurrences of each letter are:")
displayCounts(counts)
# Create a list of characters
def createList():
chars = []
for i in range(100):
chars.append(RandomCharacter.getRandomLowerCaseLetter())
return chars
# Display the list of characters
def displayList(chars):
for i in range(len(chars)):
if (i + 1) % 20 == 0: # Display 20 chars on each line
print(chars[i])
else:
print(chars[i], end = ' ')
# Create the count list
def countLetters(chars):
counts = 26 * [0]
# For each lowercase letter in the list, count it
for i in range(len(chars)):
counts[ord(chars[i]) - ord('a')] += 1 # 这里比较巧妙,将chars列表中的字幕转成ASCII码,减去‘a’的ASCII码后,正好对应count列表位置
return counts
# Display counts
def displayCounts(counts):
for i in range(len(counts)):
if (i + 1) % 10 == 0:
print(counts[i], chr(i + ord('a')))
else:
print(counts[i], chr(i + ord('a')),end = ' ')
main()
运行结果:
上面的代码中需要注意这里的代码,
for i in range(len(chars)):
counts[ord(chars[i]) - ord('a')] += 1
比较巧妙,将chars列表中的字幕转成ASCII码,减去‘a’的ASCII码后正好好对应count列表位置。虽然解决了问题,但是不太有利于代码的阅读和理解。
做下对比,左边的代码比较有利于理解和阅读,右边的代码比较“聪明”,我感觉我写的话还是会老老实实用左边的写法。
------------------------------------小节分割线------------------------------------------------------------
10.10 Searching Lists 查询列表
list类型的数据可以用index 方式来查询元素所在的位置,如
>>>lst = [1, 2, 3]
>>>lst.index(2)
1
对于查询我们先从线性查询开始,就是从列表的第一位开始逐一查找
10.10.1 The Linear Search Approach 线性查找方法
LISTING 10.9 LinearSearch.py
按照列表index逐一查找key元素,如果找到返回index号,历遍完整个列表后没有找到,返回-1
def linearSearch(lst, key):
for i in range(len(lst)):
if key == lst[i]:
return i
return -1
lst = [1, 4, 4, 2, 5, -3, 6, 2]
i = linearSearch(lst, 4) # Returns 1
j = linearSearch(lst, -4) # Returns -1
k = linearSearch(lst, -3) # Returns 5
线性查找会随着列表变长,查找时间也会变长,线性查找效率低下。
10.10.2 The Binary Search Approach 二分查找方法
二分查找有一个前提,就是列表已经是排好序的,假设是从小到大排序的。
LISTING 10.10 BinarySearch.py
def binarySearch(lst, key):
low = 0
high = len(lst) - 1
while high >= low:
mid = (low + high) // 2
if key > lst[mid]:
low = mid + 1
elif key == lst[mid]:
return mid
else:
high = mid - 1
return -1
lst = [2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70, 79]
i = binarySearch(lst, 2) # Returns 0
j = binarySearch(lst, 11) # Returns 4
k = binarySearch(lst, 12) # Returns –1
l = binarySearch(lst, 1) # Returns –1
m = binarySearch(lst, 3) # Returns –1
书上函数最后是return -low -1,我不知道是为什么,觉得return -1也没问题,所以就按照自己的思路写了代码,如果有知道的朋友可以解释一下。
-------------------------------小节分割线---------------------------------------
10.11 Sorting Lists 列表排序
排序是很基础的算法问题,这里书中就简单介绍两种排序算法:
1.Selection Sorting (选择排序)
2.Insert Sorting(插入排序)
因为这方面有其他专门的教材,我自己目前只需要了解,不需要掌握,这一小节暂时跳过,等以后有精力了再来攻克。
-------------------------------小节分割线-----------------------------------------------
10.12 Case Study: Bouncing Balls
这个例题结合Tkinter的GUI编程和Class类定义,通过界面“+”,“-”来控制小球的速度。
from tkinter import * # Import all definitions from tkinter
from random import randint
# Return a random color string in the form #RRGGBB
def getRandomColor():
color = "#"
for j in range(6):
color += toHexChar(randint(0,15))
return color
# Convert an integer to a single hex digit in a character
def toHexChar(hexValue):
if 0 <= hexValue <= 9:
return chr(hexValue + ord('0')) # 注意数值是如何结合到ASXII表中的数字部分,以“0”的ASCII码为“锚点”
else:
return chr(hexValue + ord('A') - 10) # 这里的“锚点”位置应为'A'的ASCII码-10
# Define a Ball class
class Ball:
def __init__(self):
self.x = 0 # Starting center position
self.y = 0
self.dx = 2 # Move right by default
self.dy = 2 # Move down by default
self.radius = 3 # The radius is fixed
self.color = getRandomColor() # Get random color
class BounceBalls: # 将BounceBalls的图形窗口作为class来定义
def __init__(self):
self.ballList = []
window = Tk() # 创建GUI窗口
window.title("Bouncing Balls") # Set a title
self.width = 350 # Width of the self.canvas
self.height = 150 # Height of the self.canvas
self.canvas = Canvas(window, bg = "white", width = self.width, height = self.height) # 在window中创建一个canvas
self.canvas.pack() # 显示canvas
frame = Frame(window) # 在window中创建一个frame
frame.pack() # 显示frame
btStop = Button(frame, text = "Stop", command = self.stop) # 在frame中创建一个button
btStop.pack(side = LEFT)
btResume = Button(frame, text = "Resume", command = self.resume)
btResume.pack(side = LEFT)
btAdd = Button(frame, text = "+", command = self.add)
btAdd.pack(side = LEFT)
btRemove = Button(frame, text = "-", command = self.remove)
btRemove.pack(side = LEFT)
self.sleepTime = 100
self.isStopped = False # 为什么isStopped()函数可以让动画停下来
self.animate()
window.mainloop()
def stop(self):
self.isStopped = True
def resume(self):
self.isStopped = False
self.animate()
def add(self):
self.ballList.append(Ball()) # 添加一个Ball类到列表中
def remove(self):
self.ballList.pop()
def animate(self):
while not self.isStopped:
self.canvas.after(self.sleepTime) # Sleep
self.canvas.update()
self.canvas.delete("ball")
for ball in self.ballList:
self.redisplayBall(ball)
def redisplayBall(self, ball):
if ball.x > self.width or ball.x < 0:
ball.dx = -ball.dx
if ball.y > self.height or ball.y < 0:
ball.dy = -ball.dy
ball.x += ball.dx
ball.y += ball.dy
self.canvas.create_oval(ball.x - ball.radius, ball.y - ball.radius, ball.x + ball.radius, ball.y + ball.radius, fill = ball.color, tags = "ball")
BounceBalls()
本章小测验 TEST QUESTIONS
本章编程练习 PROGRAMMING EXERCISES
(本章完)