如何改变子序列?

时间:2015-01-07 18:12:33

标签: python iterator generator

假设我有一个函数,它接受一个生成器并生成一个新生成器。例如,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 似乎更难。

3 个答案:

答案 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