【问题标题】:More pythonic way to iterate through two lists switching directions after every line?在每行之后迭代两个列表切换方向的更多pythonic方式?
【发布时间】:2020-05-14 21:41:37
【问题描述】:

我正在编写一些代码,它以一种奇怪的方式遍历两个列表。我的目标是使用索引i 遍历所有列表a,然后使用索引j 循环遍历b 并来回交替。 IE。我想按顺序遍历对:

(0,0),...,(n,0),(0,1)...(0,m),(1,1)...,(n,1),(1,2)...,(1,m),(2,2),...,(n,m)

我现在的代码是这样写的:

while i+j < len(a) + len(b) -2:
    #do stuff
    if direction_toggle:
        if i + 1 > len(a):
            direction_toggle = not direction_toggle
            i = j
        else:
            i += 1
    else:
        if j + 1 > len(b):
            direction_toggle = not direction_toggle
            j = i + 1
        else:
            j += 1

但是,我想更pythonic,并遵循

的格言

平面比嵌套好。

我想写一些看起来更像这样的东西:

while i+j < len(a) + len(b) -2:
    #do stuff
    if direction_toggle:
        var, length = i, len(a)
    else:
        var, length = j, len(b)
    if var + 1 > length:
        direction_toggle = not direction_toggle
    else:
        var += 1

所以我的问题是:有没有一种方法可以实现相同的目标但减少重复性,并去除一层嵌套?总的来说,我的代码非常简单,但似乎无法避免以两种不同的方式重复自己,是我遗漏了什么,还是我的实现实际上是实现这一目标的最糟糕的方式?

PS 我希望这不是重复的,我找不到任何其他问题解决 这个主题。

为澄清而编辑:我的具体要求是在点击(i, j) 之前处理(i, j-1)(i-1, j)(i-1, j-1)。满足此要求的任何可能的迭代路径都将起作用。如果您有兴趣,这是因为我正在尝试实现 DTW 算法,其中矩阵中分配的每个值都取决于先前的相邻值。

【问题讨论】:

    标签: python iteration nested-lists


    【解决方案1】:

    如果您将数字按 m 列网格排列在 n 行中,您可以通过沿着第一列走,然后穿过第一行(从第 1 列开始),然后沿着第二列走(从第 1 行开始),然后穿过第二行(从第 2 列开始),然后向下穿过第三列(从第 2 行开始),等等。这只是实现了。

    def gen_tuples(n_rows, n_cols):
        row = col = 0
        while row <= n_rows and col <= n_cols:
            for i in range(row, n_rows + 1):
                yield (i, col)
    
            for j in range(col + 1, n_cols + 1):
                yield (row, j)
    
            row += 1
            col += 1
    
    
    list(gen_tuples(5, 3))
    
    [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0),
     (0, 1), (0, 2), (0, 3),
     (1, 1), (2, 1), (3, 1), (4, 1), (5, 1),
     (1, 2), (1, 3),
     (2, 2), (3, 2), (4, 2), (5, 2),
     (2, 3),
     (3, 3), (4, 3), (5, 3)]
    

    【讨论】:

    • 虽然您的解决方案解决了我的问题,但我正在寻找一些不同的东西。对我来说,这意味着使用#do this 部分,并将其移动到函数调用中,这最终可能比我原来的更混乱。所以像你建议的那样连续迭代对我来说并不是真正的答案,我认为我的代码工作的唯一方法是增加相关变量after #do this。不过我很欣赏这个解决方案,它看起来很整洁!
    • @Mathchiller “美丽胜于丑陋”、“简单胜于复杂”和“可读性很重要”呢?
    • 我猜你是对的,我可以gen_tuples 然后遍历该列表。
    • 我认为您应该将and 更改为or,以便同时获得(5,3),目前已排除在外。
    • @Mathchiller 查看我的更新 -- 我将 &lt; 更改为 &lt;=
    【解决方案2】:

    根据您的“为清晰而编辑”

    我的具体要求是在命中(i,j)之前处理(i,j-1),(i-1,j)和(i-1,j-1)。

    这可以通过

    for i in range(n + 1):
        for j in range(m + 1):
             do_stuff()
    

    如果我这次理解正确,如果这是一个 5 x 4 (n = 4, m = 3) 矩阵,您基本上需要以下顺序:

     0 5  6  7
     1 8  12 13
     2 9  14 17
     3 10 15 18
     4 11 16 19
    

    你应该能够递归地解决这个问题。

    def stripes(r, c, r0=0, c0=0, flip=False):
        for row in range(r0, r + 1):
            yield (row, c0) if not flip else (c0, row)
        if r0 <= r:
            yield from stripes(c, r, r0=c0 + 1, c0=r0, flip=not flip)
    

    然后:

    >>> list(stripes(4, 3))
    [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0),
     (0, 1), (0, 2), (0, 3),
     (1, 1), (2, 1), (3, 1), (4, 1),
     (1, 2), (1, 3),
     (2, 2), (3, 2), (4, 2),
     (2, 3),
     (3, 3), (4, 3)]
    

    或针对您的特定用例:

    cache = {}
    for r, c in stripes(R, C):
        inputs = (r - 1, c), (r - 1, c - 1), (r, c - 1)
        a, b, c = (cache.get((x, y)) for x, y in inputs)
        cache[(r, c)] = do_stuff(a, b, c)
    

    【讨论】:

    • 我想我应该编辑我的问题以澄清和更具体(我会在一分钟内完成)。但是,您的解决方案不能解决我的问题。我要求在到达i, j 之前处理i, j-1i-1, ji-1, j-1。您的解决方案在此条件下失败,因为它在 (2,1) 之前命中 (3,1)
    • 啊!那我误会了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 2014-03-21
    • 2022-08-18
    相关资源
    最近更新 更多