【问题标题】:Being less repetitive with try - except尝试减少重复性 - 除了
【发布时间】:2020-12-29 19:02:47
【问题描述】:

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

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 工具而不在每种情况下都使用它。

附:这是一道古老的考试题,我们被告知我们不能导入任何模块,我们将根据效率评分

【问题讨论】:

  • 轻微的挑剔:你应该避免使用像map 这样的关键字作为变量,因为你可能真的想在你的函数体中将它们用于它们的原始目的。更好的名字可能是matrixgrid

标签: python function matrix repeat try-except


【解决方案1】:

异常处理应该保留给……嗯……异常。如果您的常规控制流依赖于异常,则您可能做错了什么*。

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

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

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

【讨论】:

    【解决方案2】:

    将通用代码重构为辅助函数。请注意,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))
    

    【讨论】:

      【解决方案3】:

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

      1. 正如mrks's answer 中所述,您不需要在此处使用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/except 来检查坐标是否在地图的域中:x_coord &lt; len(map) and y_coord &lt; len(map[0])

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-27
        • 2012-09-02
        • 1970-01-01
        • 1970-01-01
        • 2012-05-22
        • 2021-04-13
        相关资源
        最近更新 更多