减少重复尝试 - 除了

Being less repetitive with try - except

我想知道如何压缩这段代码:

def chain_reaction(map, coord):

    explosion = map[coord[0]][coord[1]]
    map[coord[0]][coord[1]] = 0

    for i in range(1, explosion + 1):
        try:
            if map[coord[0] + i][coord[1]] != 0:
                chain_reaction(map, (coord[0] + i, coord[1]))
        except IndexError:
            pass

        try:
            if map[coord[0] - i][coord[1]] != 0:
                chain_reaction(map, (coord[0] - i, coord[1]))
        except IndexError:
            pass

        try:
            if map[coord[0]][coord[1] + i] != 0:
                chain_reaction(map, (coord[0],coord[1] + i))
        except IndexError:
            pass

        try:
            if map[coord[0]][coord[1] - i] != 0:
                chain_reaction(map, (coord[0], coord[1] - i))
        except IndexError:
            pass

        try:
            map[coord[0] + i][coord[1]], map[coord[0] - i][coord[1]] = 0, 0
            map[coord[0]][coord[1] + i], map[coord[0]][coord[1] - i] = 0, 0
        except IndexError:
            pass

    return map

map = [[0,1,0,2], [3,0,1,1], [0,1,0,1], [0,0,2,0], [1,0,0,0]]

chain_reaction(map, (3,2))

脚本的目标是在给定地图的情况下模拟爆炸的连锁反应,其中任何正整数都是炸弹,其值就是炸弹的大小。如果爆炸击中另一颗炸弹,它会导致那颗炸弹爆炸。

我们也给了第一次爆炸作为坐标。

我的主要问题是,我似乎无法找到一种方法来仅使用 try - except 工具,而不在所有情况下都使用它。

P.S。这是一个老考题,我们被告知我们不能导入任何模块,我们将根据效率进行评分

异常处理应该保留给......好吧......异常。如果您的常规控制流依赖于异常,那么您很可能做错了什么*。

除了访问 a[x] 并检查异常,您还可以首先检查 x < len(a) 是否,即 x 是否为有效值。尝试从那里重写您的代码。

有很多方法可以在循环中查看所有四个方向,但我认为这些不一定会使代码更易于阅读。

*) 一旦您开始超越这个相对简单的代码,它就会变得更加复杂。 Python 与许多其他语言的不同之处在于它 actively uses exceptions 用于流量控制。现在,我会尽量远离它们并在您更熟悉该语言后重新访问 link。

将公共代码重构为辅助函数。请注意 map 是内置的名称。这也应该改变。另外,最后的 try 不是不必要的吗? chain_reaction 已经将这些位置设置为零...

def check(map, coord):
    try:
        if map[coord[0]][coord[1]] != 0:
            chain_reaction(map, (coord[0], coord[1]))
    except IndexError:
        pass

def chain_reaction(map, coord):

    explosion = map[coord[0]][coord[1]]
    map[coord[0]][coord[1]] = 0

    for i in range(1, explosion + 1):
        check(map, (coord[0] + i, coord[1]))
        check(map, (coord[0] - i, coord[1]))
        check(map, (coord[0],coord[1] + i))
        check(map, (coord[0], coord[1] - i))

        #try:
        #    map[coord[0] + i][coord[1]], map[coord[0] - i][coord[1]] = 0, 0
        #    map[coord[0]][coord[1] + i], map[coord[0]][coord[1] - i] = 0, 0
        #except IndexError:
        #    pass

    return map

map = [[0,1,0,2], [3,0,1,1], [0,1,0,1], [0,0,2,0], [1,0,0,0]]

chain_reaction(map, (3,2))

我认为这里有三个主要的更正:

  1. 中所述,您无需在此处使用try/except语句,因为您可以确定坐标是否在给定的map 在尝试访问值之前。

  2. 您可以更好地将流程的逻辑步骤组织到单独的函数中。

  3. 想办法概括重复代码(即DRY)。

这是我的重新想象。我将在下面解释我所做的事情:

UNIT_NEIGHBOR_OFFSETS = [
    (0, 1),
    (0, -1),
    (-1, 0),
    (1, 0),
]


def chain_reaction(map, coord):
    map_size = (len(map), len(map[0]))

    # find explosion size
    explosion = map[coord[0]][coord[1]]
    # reset explosion
    map[coord[0]][coord[1]] = 0
    

    for offset in explosion_neighbor_offsets(explosion):
        neighbor_x = coord[0] + offset[0]
        neighbor_y = coord[1] + offset[1]
        if (
            # check if X coord is in the map's domain
            neighbor_x < map_size[0]
            # check if Y coord is in the map's domain
            and neighbor_y < map_size[1]
            # check if there is explosion at neighbor coord
            and map[neighbor_x][neighbor_y] > 0
        ):
            chain_reaction(map, (neighbor_x, neighbor_y))

    return map


def explosion_neighbor_offsets(explosion_size):
    return [
        offset
        for exp_i in range(explosion_size)
        for offset in scaled_neighbor_offsets(exp_i + 1)
    ]


def scaled_neighbor_offsets(scale):
    return [
        (offset[0] * scale, offset[1] * scale)
        for offset in UNIT_NEIGHBOR_OFFSETS
    ]


map = [[0,1,0,2], [3,0,1,1], [0,1,0,1], [0,0,2,0], [1,0,0,0]]

chain_reaction(map, (3,2))

这是我所做的核心更改:

  1. 您可以使用 if 语句来检查坐标是否在地图域中,而不是 try/exceptx_coord < len(map) and y_coord < len(map[0])

  2. 我把求爆炸邻近坐标的过程分解成函数。请注意,在查看 chain_reaction 时,您现在如何可以略过寻找爆炸邻居的规则,因为我们将该逻辑隐藏在一个 描述性命名的 函数 explosion_neighbor_offsets 中。因此 reader 可以更快地理解递归性质 chain_reaction

  3. 我没有在代码中列举在四个主要方向上找到爆炸邻居的方法,而是构建了一种可以以数据形式表示这些方向的方法(即 UNIT_NEIGHBOR_OFFSETS) .这样做非常有用。考虑一下如果您的教授要求您修改原始代码,以便爆炸影响对角线上的邻居,则需要修改原始代码。不过在我的重构版本中,您所要做的就是将以下向量添加到 UNIT_NEIGHBOR_OFFSETS[(1, 1), (1, -1), (-1, 1), (-1, -1)]