使用 yield 将列表函数转换为生成器

Converting list function to generator using yield

提问人:oskros 提问时间:9/5/2023 更新时间:9/6/2023 访问量:62

问:

我正在尝试使用 yield 将 for 循环转换为迭代器,但我的尝试失败了。我不明白为什么没有给我预期的输出。有谁知道问题出在哪里?yield

尝试使用产量:

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [0, dim, 0]
        yield order
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                order[idx] += sgn
                yield order
print(list(iteration_order(2))
>>> [[0, 0, 0], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]

代码应该起作用(不使用 yield 时):

def iteration_order(dimensions):
    full_order = []
    for dim in range(dimensions):
        order = [[0, dim, 0]]
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                nxt = list(order[-1])
                nxt[idx] += sgn
                order.append(nxt)

        full_order.extend(order)
    return full_order
print(iteration_order(2))
>>> [[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]
python-3.x 产量

评论

0赞 matszwecja 9/5/2023
预期输出的逻辑是什么?
0赞 oskros 9/5/2023
它用于遍历六边形坐标系 - 例如,我可以写hex_array=np.zeros((dims*2,dims*2))i=0; for order in iteration_order(dims): hex_array[dims+ordr[0]-ordr[2], dims+ordr[1]-ordr[0]] = i; i += 1

答:

1赞 Andrej Kesely 9/5/2023 #1

IIUC,您可以做到:

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [[0, dim, 0]]
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim - 1):
                nxt = list(order[-1])
                nxt[idx] += sgn
                order.append(nxt)
        yield from order  # <-- yield from


print(list(iteration_order(2)))

指纹:

[[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]

评论

0赞 matszwecja 9/5/2023
这难道不会通过立即计算一切来破坏发电机的目的吗?
0赞 oskros 9/5/2023
我想这部分解决了这个问题,因为只计算当前维度值然后生成,而不是最初计算所有内容 - 但这不是一个完美的解决方案
2赞 Matt Pitkin 9/5/2023 #2

您需要随时复制列表,例如,

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [0, dim, 0]
        yield order
        corder = list(order)  # copy original list
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            corder = list(corder)  # copy updated list
            for _ in range(dim if j < 5 else dim-1):
                corder[idx] += sgn
                yield corder

这给出了:

print(list(iteration_order(2)))
[[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]

不出所料。

2赞 matszwecja 9/5/2023 #3

您看到的问题是因为您对所有内容都使用相同的列表。您可能会生成具有不同值的值,但生成器仍然引用了该列表并对其进行了修改,从而为您提供了奇怪的输出。如果为每个产量添加,它们将是唯一的列表,并且将按预期运行:.copy()

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [0, dim, 0]
        yield order.copy()
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                order[idx] += sgn
                yield order.copy()

print(list(iteration_order(2))) # [[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]
1赞 JonSG 9/5/2023 #4

我认为问题在于,你产生了一些尚未“完成”的东西,而这个东西是可以修改的。

退房:

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [0, dim, 0]
        yield order
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                order[idx] += sgn
                yield order

bar = []
for x in iteration_order(2):
    print(x)
    bar.append(x)

print(bar)

导致...

[0, 0, 0]
[0, 1, 0]
[1, 1, 0]
[1, 0, 0]
[1, 0, 1]
[0, 0, 1]
[0, 1, 1]
[[0, 0, 0], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]

你期望结果基本相同吗?然而,它们不是,因为最里面的循环在产生它后并没有“完成”。最简单的解决方法是生成副本。fororder

def iteration_order(dimensions):
    for dim in range(dimensions):
        order = [0, dim, 0]
        yield order.copy()
        for j in range(6):
            sgn = 1 if j % 2 == 0 else -1
            idx = j % 3
            for _ in range(dim if j < 5 else dim-1):
                order[idx] += sgn
                yield order.copy()

给你:

[0, 0, 0]
[0, 1, 0]
[1, 1, 0]
[1, 0, 0]
[1, 0, 1]
[0, 0, 1]
[0, 1, 1]
[[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0], [1, 0, 1], [0, 0, 1], [0, 1, 1]]

评论

1赞 oskros 9/6/2023
当然,屈服时没有考虑过列表的修改。我什至在原始函数中对此进行了修复。谢谢
1赞 trincot 9/6/2023 #5

正如其他人所指出的,您需要避免改变已经生成的列表;所以产生副本。

不相关,但如果你避免在内部循环中表达式,并且总是进行迭代,可以说它更优雅。将例外情况移到开头 -- 产生 [0,0,0],但 is 为零时除外:if...elserangedimdimensions

def iteration_order(dimensions):
    order = [0, 0, 0]
    if dimensions:
        yield order[:]  # Now this is the exceptional case
    for dim in range(1, dimensions):
        order[1] = dim
        for j in range(6):
            sgn = -(j % 2) or 1
            idx = j % 3
            for _ in range(dim):
                yield order[:]
                order[idx] += sgn

评论

1赞 oskros 9/6/2023
非常优雅!将第一个收益移到开头确实简化了很多。标志评估也很出色 - 非常感谢