问题描述
最近,阅读Python ,我碰到一个提到的那里 test_generators.py
标准模块,其中我找到了以下生成器: #conjoin是一个简单的回溯生成器,以Icon的
#连接控制结构命名。传递返回可迭代对象的无参数函数
#的列表。最简单的例子就是:假设传递
#函数列表[x,y,z]。然后结合行为:
#
#def g():
#values = [None] * 3
#用于x()中的值[0]:$ b $在y()中,b的值为[1]:
#为z()中的值[2]:
#yield值
#
#可能会生成值*,每次我们成功
#进入最内层循环。如果一个迭代器在
#之前失败(耗尽),那么它会回溯以从最近的包含
#迭代器(一个向左)获取下一个值,并重新开始在下一个
#插槽(泵一个新的迭代器)。当
#迭代器有副作用时,这当然是最有用的,这样每个时隙在
#上可以产生哪些值*取决于在以前的时隙迭代的值。
def simple_conjoin(gs):
values = [None] * len(gs)
def gen(i):
如果i> = len(gs):
产生值
else:
对于gs [i]()中的值[i]:
对于gen(i + 1):
产量x
(x):x
产量x
我花了一段时间才了解它是如何工作的。它使用一个可变列表 values
来存储迭代器的结果,而N + 1迭代器返回值
,它贯穿迭代器的整个链。
当我在阅读函数式编程的时候偶然发现了这段代码,我开始考虑是否可以重写这个联合生成器使用函数式编程(使用)。
有很多以函数式编写的例程(只需浏览?
谢谢
def conjoin(gs):$ b这个方法似乎有效, $ b返回[()]如果不是gs else(
(val,))+ gs [0]()后缀为suffix in conjoin(gs [1:])
)
def range3():
返回范围(3)
打印列表(conjoin([range3,range3]))
$ b输出:
[(0,0) ,(0,1),(0,2),(1,0),(1,1),(1 ,2),(2,0),(2,1),(2,2)]
示例显示可变状态的用法:
x =
def mutablerange():
global x
x + =x
返回[x + str(i)为我在范围内(3)]
打印列表(conjoin([range3,mutablerange]))
输出:(注意'x'的数量增加)
$ b (0,'x1'),(0,'x2'),(1,'xx0'),(1, 'xx1'),(1,'xx2'),(2,'xxx0'),(2,'xxx1'),(2,'xxx2')]
pre>
如果我们使用
itertools.product
:x =
$ p
打印列表(itertools.product(range3(),mutablerange()))
$ b结果如下:
$ $ $ $ $ $ $ $ $ $ $([0,'x0 ''),(1,'x1'),(1,'x2'),(1,'x0'), ,(2,'x1'),(2,'x2')]
早期看到,
itertools.product
会缓存迭代器返回的值。Recently, reading Python "Functional Programming HOWTO", I came across a mentioned there
test_generators.py
standard module, where I found the following generator:# conjoin is a simple backtracking generator, named in honor of Icon's # "conjunction" control structure. Pass a list of no-argument functions # that return iterable objects. Easiest to explain by example: assume the # function list [x, y, z] is passed. Then conjoin acts like: # # def g(): # values = [None] * 3 # for values[0] in x(): # for values[1] in y(): # for values[2] in z(): # yield values # # So some 3-lists of values *may* be generated, each time we successfully # get into the innermost loop. If an iterator fails (is exhausted) before # then, it "backtracks" to get the next value from the nearest enclosing # iterator (the one "to the left"), and starts all over again at the next # slot (pumps a fresh iterator). Of course this is most useful when the # iterators have side-effects, so that which values *can* be generated at # each slot depend on the values iterated at previous slots. def simple_conjoin(gs): values = [None] * len(gs) def gen(i): if i >= len(gs): yield values else: for values[i] in gs[i](): for x in gen(i+1): yield x for x in gen(0): yield x
It took me a while to understand how it works. It uses a mutable list
values
to store the yielded results of the iterators, and the N+1 iterator return thevalues
, which passes through the whole chain of the iterators.As I stumbled into this code while reading about functional programming, I started thinking if it was possible to rewrite this conjoin generator using functional programming (using functions from the
itertools
module).There are a lot of routines written in functional style (just glance at the end of this article in the Recipes section).But, unfortunately, I haven't found any solution.
So, is it possible to write this conjoin generator using functional programming just using the
itertools
module?Thanks
解决方案This seems to work, and it's still lazy:
def conjoin(gs): return [()] if not gs else ( (val,) + suffix for val in gs[0]() for suffix in conjoin(gs[1:]) ) def range3(): return range(3) print list(conjoin([range3, range3]))
Output:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
Example usage to show mutable state:
x = "" def mutablerange(): global x x += "x" return [x + str(i) for i in range(3)] print list(conjoin([range3, mutablerange]))
Output: (watch the increasing number of 'x's)
[(0, 'x0'), (0, 'x1'), (0, 'x2'), (1, 'xx0'), (1, 'xx1'), (1, 'xx2'), (2, 'xxx0'), (2, 'xxx1'), (2, 'xxx2')]
And if we use
itertools.product
:x = "" print list(itertools.product(range3(), mutablerange()))
the result is the following:
[(0, 'x0'), (0, 'x1'), (0, 'x2'), (1, 'x0'), (1, 'x1'), (1, 'x2'), (2, 'x0'), (2, 'x1'), (2, 'x2')]
So, one clearly see, that
itertools.product
caches the values returned by the iterator.这篇关于在功能风格上作出的Conjoin功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!