在因特网之前,人们也许仍有办法与世界另一边的某人下一盘国际象棋。 每个棋手在自己家里放好一个棋盘,然后轮流向对方寄出明信片, 描述每一着棋。要做到这一点,棋手需要一种方法, 无二义地描述棋盘的状态,以及他们的着法。 在“代数记谱法”中,棋盘空间由一个数字和字母坐标确定, 如下图所示:代数记谱法中棋盘的坐标。
棋子用字母表示: K 表示王, Q 表示后, R 表示车,B 表示象, N 表示马。 描述一次移动,用棋子的字母和它的目的地坐标。 一对这样的移动表示一个回合(白方先下), 例如,棋谱2.Nf3 Nc6表明在棋局的第二回合, 白方将马移动到f3,黑方将马移动到c6。
代数记谱法还有更多内容,但要点是可以用它无二义地描述象棋游戏, 不需要站在棋盘前。对手甚至可以在世界的另一边! 实际上,如果记忆力很好,甚至不需要物理的棋具: 只需要阅读寄来的棋子移动,更新心里想的棋盘。
计算机有很好的记忆力。现在计算机上的程序,
很容易存储几百万个像'2. Nf3 Nc6'
这样的字符串。
这就是为什么计算机不用物理棋盘就能下象棋。
它们用数据建模来表示棋盘,
可以编写代码来使用这个模型。
这里就可以用到列表和字典。可以用它们对真实世界建模, 例如棋盘。作为第一个例子, 将使用比国际象棋简单一点的游戏:井字棋。
井字棋盘
井字棋盘看起来像一个大的井字符号(#),有9个空格, 可以包含 X 、0或空。 要用字典表示棋盘,可以为每个空格分配一个字符串键, 如下图所示:井字棋盘的空格和它们对应的键。
可以用字符串值来表示,棋盘上每个空格有什么:
'X'
、'0'
或空格字符)。因此,
需要存储9个字符串。可以用一个字典来做这事。
带有键 'top-R'
的字符串表示右上角,
带有键 'low-L'
的字符串表示左下角,
带有键 'mid-M'
的字符串表示中间,以此类推。
这个字典就是表示井字棋盘的数据结构。
将这个字典表示的棋盘保存在名为
theBoard
的变量中。打开一个文件编辑器窗口,
输入以下代码,并保存为 ticTacToe.py
:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
保存在 theBoard
变量中的数据结构,
表示了下图中的井字棋盘:一个空的井字棋盘。
因为 theBoard
变量中每个键的值都是单个空格字符,
所以这个字典表示一个完全干净的棋盘。
如果玩家 X
选择了中间的空格,
就可以用下面这个字典来表示棋盘:
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
'mid-L': ' ', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': ' '}
theBoard
变量中的数据结构现在表示下图中的井字棋盘。
一个玩家O获胜的棋盘上,他将O横贯棋盘的顶部,看起来像这样:
theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O',
'mid-L': 'X', 'mid-M': 'X', 'mid-R': ' ',
'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}
theBoard变量中的数据结构现在表示下图中的井字棋盘:玩家O获胜。
当然,玩家只看到打印在屏幕上的内容,而不是变量的内容。
创建一个函数,将棋盘字典打印到屏幕上。
将下面代码添加到 ticTacToe.py
(新代码是黑体的):
theBoard = { 'top-L':' ','top-M':' ','top-R':' ',
'mid-L':' ','mid-M':' ','mid-R':' ',
'low-L':' ','low-M':' ','low-R':' ',
}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
printBoard(theBoard)
| | -+-+- | | -+-+- | |
运行这个程序时, printBoard()
将打印出空白井字棋盘。
printBoard()
函数可以处理传入的任何井字棋数据结构。
尝试将代码改成以下的样子:
theBoard ={'top-L':'O','top-M':'O', 'top-R':'O', 'mid-L': 'X', 'mid-M':'X', 'mid-R':' ', 'low-L': ' ','low-M': ' ', 'low-R': 'X'}
def printBoard(board):
print(board['top-L'] + '|'+ board['top-M']+ '|'+ board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|'+ board['mid-M']+ '|'+ board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|'+ board['low-M']+ '|'+ board['low-R'] )
printBoard(theBoard)
O|O|O -+-+- X|X| -+-+- | |X
现在运行该程序,新棋盘将打印在屏幕上。
因为创建了一个数据结构来表示井字棋盘,
编写了 printBoard()
中的代码来解释该数据结构,
所以就有了一个程序,对井字棋盘进行了“建模”。
也可以用不同的方式组织数据结构
(例如,使用 'TOP-LEFT'
这样的键来代替 'top-L'
),
但只要代码能处理数据结构,就有了正确工作的程序。
例如, printBoard()
函数预期井字棋数据结构是一个字典,
包含所有9个空格的键。假如传入的字典缺少 'mid-L'
键,
程序就不能工作了,如下面代码所示:
#此代码会报错
# theBoard ={'top-L':'O','top-M':'O', 'top-R':'O', 'mid-M':'X', 'mid-R':' ', 'low-L': ' ','low-M': ' ', 'low-R': 'X'}
# def printBoard(board):
# print(board['top-L'] + '|'+ board['top-M']+ '|'+ board['top-R'])
# print('-+-+-')
# print(board['mid-L'] + '|'+ board['mid-M']+ '|'+ board['mid-R'])
# print('-+-+-')
# print(board['low-L'] + '|'+ board['low-M']+ '|'+ board['low-R'] )
# printBoard(theBoard)
现在添加代码,允许玩家输入他们的着法。修改 ticTacToe.py
程序如下所示:
theBoard = { 'top-L':' ','top-M':' ','top-R':' ',
'mid-L':' ','mid-M':' ','mid-R':' ',
'low-L':' ','low-M':' ','low-R':' ',
}
def printBoard(board):
print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
print('-+-+-')
print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
print('-+-+-')
print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])
turn = 'X'
for i in range(9):
printBoard(theBoard)
print('Turn for ' + turn + '.Move on which space')
move = input()
theBoard[move] = turn
if turn == 'X':
turn = 'O'
else:
turn = 'X'
printBoard(theBoard)
allGuests = {'Alice': {'apples': 5, 'pretzels': 12},
'Bob': {'ham sandwiches': 3, 'apples': 2},
'Carol': {'cups': 3, 'apple pies': 1}}
def totalBrought(guests, item):
numBrought = 0
for k, v in guests.items():
numBrought = numBrought + v.get(item,0)
return numBrought
print('Number of things being brought:')
Number of things being brought:
print(' - Apples ' + str(totalBrought(allGuests, 'apples')))
- Apples 7
print(' - Cups ' + str(totalBrought(allGuests, 'cups')))
- Cups 3
print(' - Cakes ' + str(totalBrought(allGuests, 'cakes')))
- Cakes 0
print(' - Ham Sandwiches ' + str(totalBrought(allGuests, 'ham sandwiches')))
- Ham Sandwiches 3
print(' - Apple Pies ' + str(totalBrought(allGuests, 'apple pies')))
- Apple Pies 1
这似乎对一个非常简单的东西建模,可能认为不需要费事去写一个程序来做
到这一点。但是要认识到,这个函数 totalBrought()
可以轻易地处理一个字典,
其中包含数千名客人,每个人都带来了 “数千种”不同的野餐食物。
这样用这种数据结构来保存信息,
并使用 totalBrought()
函数,就会节约大量的时间!
可以用自己喜欢的任何方法,用数据结构对事物建模, 只要程序中其他代码能够正确处理这个数据模型。在刚开始编程时, 不需要太担心数据建模的“正确”方式。 随着经验增加,可能会得到更有效的模型, 但重要的是,该数据模型符合程序的需要。
小结
在本章中学习了字典的所有相关知识。列表和字典是这样的值, 它们可以包含多个值,包括其他列表和字典。字典是有用的, 因为可以把一些项(键)映射到另一些项(值), 它不像列表,只包含一系列有序的值。字典中的值是通过方括号访问的, 像列表一样。字典不是只能使用整数下标,而是可以用各种数据类型作为键: 整型、浮点型、字符串或元组。通过将程序中的值组织成数据结构, 可以创建真实世界事物的模型。井字棋盘就是这样一个例子。
这就介绍了Python 编程的所有基本概念!在后面的部分, 将继续学习一些新概念, 可以开始编写一些有用的程序,让一些任务自动化。 可能会不觉得自己有足够的 Python 知识,来实现页面下载、 更新电子表格,或发送文本消息。但这就是 Python 模块要做的事! 这些模块由其他程序员编写,提供了一些函数,让这些事情变得容易。 所以学习如何编写真正的程序,才能实现有用的自动化任务。