为什么我的笛卡尔积函数不起作用?
Why doesnt my cartesian product function work?
考虑以下函数,其输出应该是可迭代序列的笛卡尔积:
def cart(*iterables):
out = ((e,) for e in iterables[0])
for iterable in iterables[1:]:
out = (e1 + (e2,) for e1 in out for e2 in iterable)
return out
用列表推导替换生成器推导时工作正常。当只有 2 个可迭代对象时也有效。但是当我尝试
print(list(cart([1, 2, 3], 'ab', [4, 5])))
我明白了
[(1, 4, 4), (1, 4, 5), (1, 5, 4), (1, 5, 5),
(2, 4, 4), (2, 4, 5), (2, 5, 4), (2, 5, 5),
(3, 4, 4), (3, 4, 5), (3, 5, 4), (3, 5, 5)]
为什么是这个而不是笛卡尔积?
您正在创建 生成器表达式 ,直到 for iterable in iterables[1:]:
循环的下一次迭代才对其进行迭代。他们正在使用 闭包,在运行时查找。
生成器表达式在这方面本质上是小函数,它们创建自己的作用域,父作用域中的任何名称都需要被视为闭包才能工作。 'function' 在迭代时执行,然后才需要闭包并将其解析为引用变量的 current 值。
所以你创建一个这样的生成器表达式:
(e1 + (e2,) for e1 in out for e2 in iterable)
其中 iterable
是从父作用域(您的函数局部变量)获取的闭包。但是查找直到循环时的下一次迭代才完成,此时 iterable
是序列中的下一个元素.
因此,对于 [1, 2, 3], 'ab', [4, 5]
的输入,您在 iterable = 'ab'
时创建了一个生成器表达式,但是当您实际迭代时,for
循环已经分配了一个新值,现在是iterable = [4, 5]
。当您最终遍历最终(链式)生成器时,只有对 iterable
的最后一次赋值才算数。
您在 iterables[0], iterables[-1] * len(iterables) - 1
上有效地创建了一个产品; iterables[1]
到 iterables[-2]
被完全跳过,全部替换为 iterables[-1]
。
您可以使用生成器 函数 来避免关闭问题,传入 iterable
以绑定到本地:
def gen_step(out, iterable):
for e1 in out:
for e2 in iterable:
yield e1 + (e2,)
def cart(*iterables):
out = ((e,) for e in iterables[0])
for iterable in iterables[1:]:
out = gen_step(out, iterable)
return out
您可以对返回生成器表达式的 lambda 执行相同的操作:
def cart(*iterables):
out = ((e,) for e in iterables[0])
for iterable in iterables[1:]:
out = (lambda it=iterable: (e1 + (e2,) for e1 in out for e2 in it))()
return out
考虑以下函数,其输出应该是可迭代序列的笛卡尔积:
def cart(*iterables):
out = ((e,) for e in iterables[0])
for iterable in iterables[1:]:
out = (e1 + (e2,) for e1 in out for e2 in iterable)
return out
用列表推导替换生成器推导时工作正常。当只有 2 个可迭代对象时也有效。但是当我尝试
print(list(cart([1, 2, 3], 'ab', [4, 5])))
我明白了
[(1, 4, 4), (1, 4, 5), (1, 5, 4), (1, 5, 5),
(2, 4, 4), (2, 4, 5), (2, 5, 4), (2, 5, 5),
(3, 4, 4), (3, 4, 5), (3, 5, 4), (3, 5, 5)]
为什么是这个而不是笛卡尔积?
您正在创建 生成器表达式 ,直到 for iterable in iterables[1:]:
循环的下一次迭代才对其进行迭代。他们正在使用 闭包,在运行时查找。
生成器表达式在这方面本质上是小函数,它们创建自己的作用域,父作用域中的任何名称都需要被视为闭包才能工作。 'function' 在迭代时执行,然后才需要闭包并将其解析为引用变量的 current 值。
所以你创建一个这样的生成器表达式:
(e1 + (e2,) for e1 in out for e2 in iterable)
其中 iterable
是从父作用域(您的函数局部变量)获取的闭包。但是查找直到循环时的下一次迭代才完成,此时 iterable
是序列中的下一个元素.
因此,对于 [1, 2, 3], 'ab', [4, 5]
的输入,您在 iterable = 'ab'
时创建了一个生成器表达式,但是当您实际迭代时,for
循环已经分配了一个新值,现在是iterable = [4, 5]
。当您最终遍历最终(链式)生成器时,只有对 iterable
的最后一次赋值才算数。
您在 iterables[0], iterables[-1] * len(iterables) - 1
上有效地创建了一个产品; iterables[1]
到 iterables[-2]
被完全跳过,全部替换为 iterables[-1]
。
您可以使用生成器 函数 来避免关闭问题,传入 iterable
以绑定到本地:
def gen_step(out, iterable):
for e1 in out:
for e2 in iterable:
yield e1 + (e2,)
def cart(*iterables):
out = ((e,) for e in iterables[0])
for iterable in iterables[1:]:
out = gen_step(out, iterable)
return out
您可以对返回生成器表达式的 lambda 执行相同的操作:
def cart(*iterables):
out = ((e,) for e in iterables[0])
for iterable in iterables[1:]:
out = (lambda it=iterable: (e1 + (e2,) for e1 in out for e2 in it))()
return out