假设我有一个函数,它接受一个生成器并生成一个新生成器。例如,pair(gen)
将序列1, 2, 3, 4, ...
转换为(1, 2), (3, 4), ...
。
现在我想采用一些序列的生成器并将转换应用于其子序列,同时保留其他元素。例如,如果gen
生成序列1, 2, 3, 4, 5, ...
,并且我想在偶数元素上运行pair
,那么withEven(gen, pair)
将生成1, 3, (2, 4), 5, ...
。它应该知道需要多少个元素pair
,也不应该缓存所有接收到的奇数,直到对产生。它应该从主序列中获取下一个项目,如果它是奇数,则产生它,如果它是偶数,则将它提供给变换器并重复。
withEven
可以定义吗?
如果pair
定义为
def pair(gen):
prev = None
for x in gen:
if prev:
yield (prev, x)
prev = None
else:
prev = x
然后,相当于withEvenRunPair(gen)
的{{1}}很难定义为
withEven(gen, pair)
然而,定义通用def withEvenRunPair(gen):
prev = None
for x in gen:
if x % 2 == 1:
yield x
else:
if prev:
yield (prev, x)
prev = None
else:
prev = x
似乎更难。
答案 0 :(得分:0)
我想你可以这样做:
from itertools import islice, izip, izip_longest, tee
def grouper(iterable, n, fillvalue=None):
# https://docs.python.org/2/library/itertools.html#recipes
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
def withEvenRunPair(gen):
it1, it2 = tee(gen)
odd = islice(it1, 0, None, 2)
even = islice(it2, 1, None, 2)
for a, b in izip_longest(grouper(odd, 2), grouper(even, 2)):
yield a
yield b
gen = (x for x in xrange(1, 13))
for x in withEvenRunPair(gen):
print x
<强>输出:强>
(1, 3)
(2, 4)
(5, 7)
(6, 8)
(9, 11)
(10, 12)
答案 1 :(得分:0)
你的意思是什么?
def pair(sequence, selector=bool, fill=None, error_on_fill=False):
sentinel = object()
previous = sentinel
for item in sequence:
if not selector(item):
yield item
continue
if previous is not sentinel:
yield previous, item
previous = sentinel
else:
previous = item
if previous is not sentinel:
if error_on_fill:
raise ValueError("Final selected item missing a paired valued")
yield previous, fill
result = list(pair(range(10), selector=(lambda x: x % 2 == 0)))
assert result == [1, (0, 2), 3, 5, (4, 6), 7, 9, (8, None)]
答案 2 :(得分:0)
我终于明白了,虽然它需要对pair
和其他转换进行一些更改。
停止计算并稍后恢复计算的唯一方法是yield
。因此,如果我们用某种input_generator.next()
替换yield
,我们就可以控制值转换为转换的速度。
首先我定义一个请求对象
class Get(object):
def isset(self):
return hasattr(self, 'value')
def set(self, x):
self.value = x
我现在将<{1}}替换为
n = foo.next()
例如,我的问题中的g = Get()
yield g
if not g.isset():
break
n = g.value
将成为
pair
这种转变很容易变成适当的转变
def pair(): #notice that there isn't an input generator any more
prev = None
while True: #there was a next() hidden inside 'for'
g = Get()
yield g
if not g.isset():
break
elif prev:
yield (prev, g.value)
prev = None
else:
prev = g.value
最后,我们可以定义def withGen(fn, gen):
for f in fn: #get the next request or result
if isinstance(f, Get):
f.set(gen.next()) #answer requests with gen.next()
else:
yield f #yield results
print [x for x in withGen(pair(), [1, 2, 3, 4].__iter__())]
#prints [(1, 2), (3, 4)]
withSelector
我甚至可以对两个选择器进行排序。例如,如果我将def withSelector(select, fn):
f = None #we need to remember the last unanswered request
while True:
if not f: #if last request has been answered...
f = fn.next() #get the next request, or the result
if isinstance(f, Get): #if it's a request...
g = Get()
yield g #ask for another input value
if not g.isset():
break
elif select(g.value): #if the values matches selector...
f.set(g.value) #feed it to transformation
f = None #and mark the request as answered
else:
yield g.value #otherwise just yield it
else: #if transformation yielded a result
yield f #yield it
f = None #and note that there is no request waiting
arr = range(1, 18)
print [x for x in withGen(withSelector(isEven, pair()), arr.__iter__())]
#prints [1, 3, (2, 4), 5, 7, (6, 8), 9, 11, (10, 12), 13, 15, (14, 16), 17]
定义为add3
三个yield
,然后将Get()
定义为其结果的总和,我可以
yield