12. 列表

12.1 目标

  • 理解列表的概念
  • 学习列表的基本操作
  • 学习利用索引

列表是非常有用的数据结构,它可以用来 同时 存储多个整数值,GPS坐标,逻辑值,和字符串. 不同类型的值可以被组合在一起,列表甚至可以包含其他列表.例如,列表可以储存机器人的路径,迷宫中宝石的位置,障碍物的位置和形状等等.列表中的对象是有顺序的,它可以被添加到列表末尾,通过索引获取它,或者删除指定位置的元素.

12.2 与Python的兼容性

Karel的列表是 Python 编程语言中列表的一个子集.换言之,任何在此处学到的东西都适用于 Python ,但是 Python 还提供很多Karel没有提供的额外功能.

12.3 创建列表

我们使用方括号创建列表.空列表 U 创建如下:

U = []

也可以创建非空列表:

V = [1, 2, 3, 4, 5]

我们也可以使用变量创建列表:

c = 100
W = [0, 50, c]
print W

输出为:

[0, 50, 100]

整数可以与字符串混合:

X = [1,"Hello", 2]

列表还可以包含其他列表作为元素:

Y = [1,"Hello", 2, [1, 2, 3]]

可以按如下方式打印列表:

print"This is the list Y:", Y

上述代码输出为:

This is the list Y: [1,"Hello", 2, [1, 2, 3]]

12.4 通过索引获取列表元素

列表元素可以通过索引获取,以便打印,赋值给其他变量或用在他处.索引总是从0开始.换言之, L[0] 是列表 L 的第一个元素, L[1] 是第二个元素, 以此类推.请见下例:

L = [8, 12, 16, 20]
print "First item:", L[0]
print "Second item:", L[1]
print "Third item:", L[2]
print "Fourth item:", L[3]

输出为:

First item: 8
Second item: 12
Third item: 16
Fourth item: 20

12.5 向列表追加元素

任何对象 obj (整数,文本字符,逻辑值,其他列表等)都可以通过 append() 函数添加到当前列表的末尾. 对于列表 L 方法如下:

L.append(obj)

进一步说明:

K = [1, 11]
K.append(21)
print K

输出:

[1, 11, 21]

12.6 通过pop()函数移除列表元素

列表 L 的第 i 个元素可以通过以下方法删除并且赋给新的变量 x :

x = L.pop(i)

为了展示,创建一个包含3个文本字符串”Monday”,”Tuesday”,”Wdenesday”的列表,然后删除第二个元素:

X = ["Monday","Tuesday","Wednesday"]
day = X.pop(1)
print day
print X

输出:

Tuesday
[’Monday’,’Wednesday’]

如果 pop() 函数不指定下标,它将移除列表的最后一个元素并返回它:

day = X.pop()
print day
print X

输出:

Wednesday
[’Monday’]

12.7 通过del命令删除元素

del 命令的功能类似于 pop() 函数, 但它不会保存被删除元素. 列表 L 的第 i 个元素可以通过如下方法删除:

del L[i]

请见下例:

L = ["Monday","Tuesday","Wednesday"]
del L[0]
print L
del L[0]
print L

输出:

[’Tuesday’,’Wednesday’]
[’Wednesday’]

12.8 列表的长度

函数 len(X) 返回列表 X 的长度.例子如下:

M = ["John","Josh","Jim","Jane"]
n = len(M)
print "Length of the list is", n

输出:

Length of the list is 4

12.9 解析列表

在Karel中,列表可以用 for 命令解析,与 Python 的用法一样. 以下例子定义了由4个元素1,2,3,4组成的列表 M ,打印它们,并且使每个元素增加2:

M = [1, 3, 5, 7]
for n in M
  print inc(n, 2)

输出:

3
5
7
9

12.10 记录robot的游走路径

使用 第十一节 中的 measurewall 函数,修改它为记录机器人的行走路径并返回. 新的函数称为 recordpath :

# Function to record robot’s path:
def recordpath
  L=[[gpsx, gpsy]]
  while wall
    left
    if not wall
      go
      L.append([gpsx, gpsy])
      right
      if not wall
        go
        L.append([gpsx, gpsy])
        right
        if not wall
          return L

# Record the path and print it:
print recordpath

这次我们用短一点的墙,以便使结果列表不要太长:

_images/12_01.png

图1.记录机器人行走路径

运行程序输出为:

[[5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], [6, 9],
[7, 9], [8, 9], [9, 9], [9, 8], [9, 7], [8, 7], [7, 7],
[8, 7], [9, 7], [9, 6], [9, 5], [8, 5], [7, 5], [8, 5],
[9, 5], [9, 4], [9, 3], [8, 3], [7, 3], [6, 3], [6, 4]]

这便是正确的结果了,注意左下角的坐标是[0,0]

12.11 通过列表重放robot的行走轨迹

现在我们教Karel用储存的GPS坐标路径行走.确切地说,路径点将以[gpsx,gpsy]的形式储存在列表 L 中. 首先我们需要定义4个函数可以分别使机器人朝向东,南,西,北.还要实现一个程序 gotoposition ,将机器人从初始位置移动到另一个新位置NEWX, NEWY:

# Turn North:
def turnnorth
  while not north
    left

# Turn East:
def turneast
  turnnorth
  right

# Turn South:
def turnsouth
  turneast
  right

# Turn West:
def turnwest
  turnnorth
  left

# Move from the current position to
# a new position [NEWX, NEWY]:
def gotoposition
  # Horizontal direction first:
  posx = gpsx
  if posx < NEWX
    turneast
    repeat dec(NEWX, posx)
      go
  else
    turnwest
    repeat dec(posx, NEWX)
      go

  # Vertical direction:
  posy = gpsy
  if posy < NEWY
    turnnorth
    repeat dec(NEWY, posy)
      go
  else
    turnsouth
    repeat dec(posy, NEWY)
      go

正如你所见,上述程序并没有假设新的位置紧邻当前位置,这使得 gotoposition 具有更一般的应用. 最后一步很简单,只需解析列表 L ,将机器人引向新的位置:

# Parse list L, always go to the
# next position:
def playlist
  n = len(L)
  i = 0
  repeat n
    newpair = L[i]
    NEWX = newpair[0]
    NEWY = newpair[1]
    gotoposition
    inc(i)

之所以需要借助变量 newpair 是因为Karel不支持双下标索引. 接下来就可以用样例路径列表 L 来测试:

# Sample list L:
L = [[3, 10], [5, 5], [10, 10], [1, 1]]
# We need these two global variables:
NEWX = gpsx
NEWY = gpsy
# Go!
playlist

读者可以看到全局变量 NEWX , NEWY , L 的使用. 这虽不是最好的编程实践,但由于Karel语言的函数暂不接受参数,这是权宜之策. 未来将考虑实现传递函数参数功能.

12.12 记录宝石位置

宝石随机分散在迷宫周围.每个方块有可能包含多个宝石. 机器人需要沿着迷宫边走一圈,记录宝石的位置和数量,储存在一个列表中. 列表元素的形式为[gpsx,gpsy, number]. 最后打印出这个列表.

_images/12_02.png

图2.宝石随机分散在迷宫周围

以下程序可以解决这个问题.首先写一个函数 countgems 数方块中的宝石并返回其数目:

def countgems
  num = 0
  # Collect all gems:
  while gem
    get
    inc(num)

  # Put the gems back:
  repeat num
    put

  return num

用以上函数就可以获得想要的列表了:

# Locate all gems:
def locategems
  repeat 4
    while not wall
      go
      if gem
        L.append([gpsx,gpsy, countgems])
    left

  return L

# Call the main function:
L = []
listofgems = locategems
print "Here is the report:"
print listofgems

输出为:

Here is the report:
[[2, 0, 3], [4, 0, 1], [7, 0, 2], [10, 0, 7], [14, 0, 1],
[14, 2, 4], [14, 6, 4], [14, 10, 2], [11, 11, 2], [7, 11, 3],
[6, 11, 1], [2, 11, 2], [0, 11, 1], [0, 9, 1], [0, 6, 3],
[0, 3, 2]]

12.12 列表中的列表

在一些程序中我们遇见列表包含其他列表.在其他编程语言中,嵌入列表的元素可以通过多重下标访问.在Karel中,暂不支持此功能,但这根本不是个事! 看下面的例子,使用上例的列表 L ,创建只包含宝石数目(不包含坐标)的列表 L2 :

L2 = []
for x in L
  L2.append(x[2])
print "Skipping the coordinates:"
print L2