二维列表


二维列表

列表以线性顺序或单一维度跟踪多个信息片段。但是,某些系统(数字图像、棋盘游戏等)的数据存在于二维中。为了可视化这些数据,我们需要一个多维数据结构,即多维列表。

二维列表实际上只不过是列表的列表(三维列表是列表的列表的列表)。想想你的晚餐。你可以有一个一维列表,包含你吃的所有东西:

(生菜, 西红柿, 沙拉酱, 牛排, 土豆泥, 豆角, 蛋糕, 冰淇淋, 咖啡)

或者你可以有一个二维列表,包含三道菜,每道菜包含三样你吃的东西:

(生菜, 西红柿, 沙拉酱) 和 (牛排, 土豆泥, 豆角) 和 (蛋糕, 冰淇淋, 咖啡)

在列表的情况下,我们传统的一维列表看起来像这样:

myList = [0,1,2,3]

而二维列表看起来像这样:

myList = [ [0,1,2,3], [3,2,1,0], [3,5,6,1], [3,8,3,4] ]

对于我们的目的,最好将二维列表视为矩阵。矩阵可以被认为是一个数字网格,按行和列排列,有点像宾果游戏板。我们可以将二维列表写出来以说明这一点:

myList = [  [0, 1, 2, 3],
             [3, 2, 1, 0],
             [3, 5, 6, 1],
             [3, 8, 3, 4]  ]

我们可以使用这种类型的数据结构来编码关于图像的信息。例如,以下灰度图像可以由以下列表表示:

4x4 灰度网格示例

myList = [  [236, 189, 189,   0],
             [236,  80, 189, 189],
             [236,   0, 189,  80],
             [236, 189, 189,  80]  ]

遍历二维列表

要遍历一维列表的每个元素,我们使用 for 循环,即:

myList = [0,1,2,3,4,5,6,7,8,9]
for index in range(len(myList)):
  myList[index] = 0 # 将索引处的元素设置为 0

对于二维列表,为了引用每个元素,我们必须使用两个嵌套循环。这为我们提供了矩阵中每列和每行的计数器变量。

myList = [ [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8] ]

# 两个嵌套循环允许我们访问二维列表中的每个位置。
# 对于每一列 i,访问每一行 j。
for i in range(len(myList)):
  for j in range(len(myList[0])):
    myList[i][j] = 0

示例:绘制灰度图像

例如,我们可以编写一个使用二维列表绘制灰度图像的程序。

随机噪声示例(每次刷新都会生成不同的图案)

# 示例:二维列表
def setup():
    size(200,200)
    nRows = height
    nCols = width
    myList = make2dList(nRows, nCols)
    drawPoints(myList)
    
def make2dList(nRows, nCols):
    newList = []
    for row in range(nRows):
        # 为每个新行提供一个空列表
        newList.append([])
        for col in range(nCols):
            # 使每行中的每列都是 0 到 255 之间的随机整数
            newList[row].append(int(random(255)))
            
    return newList

def drawPoints(pointList):
    for y in range(len(pointList)):
        for x in range(len(pointList[0])):
            stroke(pointList[y][x])
            rect(x,y,10,10)

对象网格

二维列表也可以用来存储对象,这对于涉及某种"网格"或"棋盘"的编程草图特别方便。以下示例显示存储在二维列表中的 Cell 对象网格。每个单元格都是一个矩形,其亮度使用正弦函数在 0-255 之间振荡。

动态对象网格示例(每个单元格的亮度会振荡变化)

# 示例:对象二维数组
# 网格中的列数和行数
nCols = 10
nRows = 10

def setup():
    global nCols, nRows, grid
    size(200,200)
    grid = makeGrid()
    for i in range(nCols):
        for j in range(nRows):
            # 初始化每个对象
            grid[i][j] = Cell(i*20,j*20,20,20,i+j)
            
def draw():
    global nCols, nRows, grid
    background(0)
    # 计数器变量 i 和 j 也是列号和行号,
    # 并用作网格中每个对象构造函数的参数。
    for i in range(nCols):
        for j in range(nRows):
            # 振荡并显示每个对象
            grid[i][j].oscillate()
            grid[i][j].display()
        
# 创建一个 nCols x nRows 大小的二维列表,填充 0
def makeGrid():
    global nCols, nRows
    grid = []
    for i in range(nCols):
        # 为每一行创建一个空列表
        grid.append([])
        for j in range(nRows):
            # 用 0 填充每行中的每列
            grid[i].append(0)
    return grid

# Cell 对象
class Cell():
    # 单元格对象知道它在网格中的位置
    # 它还通过变量 x,y,w,h 知道它的大小
    def __init__(self, tempX, tempY, tempW, tempH, tempAngle):
        self.x = tempX
        self.y = tempY
        self.w = tempW
        self.h = tempH
        self.angle = tempAngle
    
    # 振荡意味着增加角度
    def oscillate(self):
        self.angle += 0.02
        
    def display(self):
        stroke(255)
        # 使用正弦波计算颜色
        fill(127+127*sin(self.angle))
        rect(self.x,self.y,self.w,self.h)