程序员入伙书——数组

从前我用过的程序示例里面,使用的数据都是分离的、独立的。就是说,每个常量和变量,各有各的用途,没有“我们是一伙的”这样的共性。而在现实世界里,成群结队地出现的数据是大多数。例如一架飞机上的旅客,一个班级的学生,奥运会的参赛国。

我见过的所有编程语言都有数组(Array,List)的概念,用来存储具有共同属性的批量数据。在Python里,数组用一对中括号包围的一群数据来表示,数据之间用逗号隔开。例如:

>>> countries = ['China', 'USA', 'Germany', 'Canada', 'Japan']
>>> countries[0]
'China'
>>> countries[3]
'Canada'
>>> countries[4]
'Japan'
>>> 
💡 Python语言允许数组的每个元素类型不同,例如字符串和数字可以混装在同一个数组里面,如[3.14, ‘China’]就是一个正常的Python数组。其它的编程语言通常没有这么大度。在实际应用中,即使是Python,也很少遇到各元素类型不同的案例。

在这里,countries就是一个数组,它有五个元素(Element),每个元素都是一个字符串表示的国名。引用这些元素的时候,我们使用一个整数,来指明它在数组里的位置,这个整数叫做下标(Index)。和大家的习惯稍有差异的是,在Python里,当我们说“数组的第一个元素”时,我们用的下标是0,第二个元素的下标是1,第三个元素的下标是2,依此类推。所以上面的程序范例里面,countries[0]打印出来的是第一个国名China,第五个国名使用的下标是4。

如果你觉得这个规则挺怪异的,想一想这个问题:进入一座楼房,爬到一楼需要爬几层楼梯?爬到五楼需要爬几层楼梯?然后你就释然了,因为对于计算机来说,数组就像楼层,一楼就是它的第一个元素,其它的楼层都是相对于一楼的偏移量。英式英语里,一楼叫做Ground Floor,二楼叫做First Floor,三楼叫做Second Floor,这就是计算机式的思维。

C/C++、Python、Perl、Java、PHP等语言里,第一个元素都是用下标0来表示。FORTRAN、BASIC、PASCAL等语言里,第一个元素是用下标1来表示。这两种表示方法都是合理的,照顾不同人群的思维习惯。

在刚才这个数组里,使用countries[5]会看到什么?

>>> countries[5]
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in 
    countries[5]
IndexError: list index out of range
>>>

Python抱怨道:越界(out of range)了。越界是不熟练的程序员常犯的错误之一,通常引起程序的崩溃。如果是操作系统级别的程序——例如Windows——你可能会看到蓝屏。

如果你想再添加一个国家名字到countires里,不能写countires[5] = ‘Turkey’——因为这样会引起越界——而是写:

>>> countries.append('Turkey')
>>> countries
['China', 'USA', 'Germany', 'Canada', 'Japan', 'Turkey']
>>> 

来,我们看看用这个数组可以干什么:

>>> len(countries)            # 看看有几个国家参赛
6
>>> countries.sort()          # 排一下奥运会入场顺序
>>> countries
['Canada', 'China', 'Germany', 'Japan', 'Turkey', 'USA']
>>> countries.index('China')  # 中国排第几个出场?注意返回的结果要加1
1
>>> 'USA' in countries        # 美国有没有参赛?
True
>>> countries.extend(['France', 'Russia'])   # 法国和俄罗斯也要参赛
>>> countries
['Canada', 'China', 'Germany', 'Japan', 'Turkey', 'USA', 'France', 'Russia']
>>> 

对数组的操作还有许多,我们将来会陆续用到。眼前不需要记得十分仔细,用到的时候自然就明白了。

数组可以成为更大的数组里的元素,当这种情况出现时,更大的数组就成了多维数组(Multi-Dimension List)。例如一个学校有三个年级,每个年级有六个班,每个班有若干学生。这就是一个三维数组(3-Dimension List)。

>>> school = [  # 三个年级   \
  [         # 一年级的六个班  \
    ['Mike', 'Tom', 'Tony'], ['Alice', 'Peter', 'Mike'],       \
    ['David', 'Mark', 'Shirley'], ['Andy', 'Sean', 'Connie'],  \
    ['Steve', 'Tony', 'Ryan'], ['Gloria', 'Tracy', 'Jane']     \
  ],     \
  [         # 二年级的六个班  \
    ['Mike', 'Tom', 'Tony'], ['Alice', 'Peter', 'Mike'],       \
    ['Lynsey', 'Mark', 'Shirley'], ['Andy', 'Sean', 'Connie'], \
    ['Steve', 'Tony', 'Ryan'], ['Gary', 'Tracy', 'Jane']       \
  ],     \
  [         # 三年级的六个班  \
    ['Mike', 'Tom', 'Tony'], ['Alice', 'Peter', 'Mike'],      \
    ['David', 'Roy', 'Shirley'], ['Andy', 'Sean', 'Connie'],  \
    ['Steve', 'Tony', 'Ryan'], ['Ray', 'Tracy', 'Justin']     \
  ]      \
]
>>> school[0][3][2]    # 一年级四班的第三位同学的名字?
'Connie'
>>> school[1][5]       # 二年级六班的所有学生?
['Gary', 'Tracy', 'Jane']
>>>

用二维数组玩一下。在数学里有个东东叫“幻方”,在一个方格阵列里填一连串的整数,每行每列及对角线的和都相等。这个奇妙的东西,是有算法的,奇数级幻方的算法尤为简单:

  1. 画一个NxN的方格阵列。
  2. 在最右边、正中间的格子里填1。
  3. 笔向右下方移动,如果移出右边界,则跳到左边第一列,如果移出下边界,则跳到顶端第一行。填下一个数字。
  4. 每填到N的倍数之后,笔向左边的格子移一格。填下一个数字。
  5. 回到第3步,直到填满整个方格阵列为止。
# 获得一个奇数
N = 0
while N <= 0 or N % 2 == 0:
    string = input("Please input an odd integer: ")
    if string.isdigit():
        N = int(string)

# 初始化一个NxN的二维数组,每个格子里暂且填上0
a = [[0] * N] * N

# 下标设置为最右侧正中间一行
i = N // 2
j = N - 1

# 从1到N*N填写:
for n in range(1, N * N + 1):
	a[i][j] = n
	if (n % N):
		i += 1      # 向下方移动
		j += 1      # 向右侧移动
		if i >= N:  # 如果移出下边界,则回到顶行
			i = 0
		if j >= N:  # 如果移出右边界,则回到左列
			j = 0
	else:               # 每当数字可以被N整除时,向左移一格
		j -= 1

# 打印幻方数组
strfmt = "%%%dd" % (len(str(N * N)) + 1)
for i in range(N):
	for j in range(N):
		print(strfmt % a[i][j], end = '')
	print()

执行一下看看,第一次我们输入3,得到的是中国古代著名的九宫格,每行每列对角线相加都等于15。第二次我们玩个大的,输入7,每行每列对角线相加都等于175。你来试试?输入多大的奇数都可以。

>>> ======================== RESTART ========================
>>> 
Please input an odd integer: 3
 4 3 8
 9 5 1
 2 7 6
>>> ======================== RESTART ========================
>>> 
Please input an odd integer: 7
 22 21 13  5 46 38 30
 31 23 15 14  6 47 39
 40 32 24 16  8  7 48
 49 41 33 25 17  9  1
  2 43 42 34 26 18 10
 11  3 44 36 35 27 19
 20 12  4 45 37 29 28
>>> 
评论关闭