【问题标题】:Sort generated numbers using another python generator使用另一个 python 生成器对生成的数字进行排序
【发布时间】:2015-02-22 02:24:48
【问题描述】:

我正在尝试使用 python 生成器来实现一种合并排序,以在生成的数字中找到最小的数字并生成下一个,这是我的示例代码:

class GeneratorSort():
    def __init__(self, *args):
        self.values = [(arg.next(), i) for i, arg in enumerate(args)]
        self.generators = args

    def generate(self):
        r, index = min(self.values)
        self.values[index] = self.generators[index].next()
        yield r


def t(l):
    for each in l:
        yield each

l1 = [2, 5, 6, 8]
l2 = [1, 4, 5, 7]
l3 = [0, 3, 9, 10]

a = GeneratorSort(t(l1), t(l2), t(l3))

但是当我尝试打印排序结果时,我只得到了0,下次出现错误:

>>> for i in a.generate():
        print i
0

这是错误:

>>> a.generate()
<generator object generate at 0x7fa7bcc37a00>
>>> a.generate().next()

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    a.generate().next()
  File "/home/hamid/projects/bfl/workspace/testo.py", line 10, in generate
    r, index = min(self.values)
TypeError: 'int' object is not iterable
>>> 

我希望从这个函数中打印出像 1,2,3,4,5 和 ... 排序的数字。有没有其他办法?

请注意,我需要使用生成器。

【问题讨论】:

    标签: python sorting generator


    【解决方案1】:

    您正在将 (value, index) 元组替换为 值:

    self.values[index] = self.generators[index].next()
    

    你需要用一个新的元组替换它:

    self.values[index] = (self.generators[index].next(), index)
    

    否则迭代赋值失败;您不能将一个 int 分配给两个变量。

    您的生成器缺少循环和空生成器的处理:

    def generate(self):
        while any(self.values):
            r, index = min(v for v in self.values if v)
            try:
                self.values[index] = (self.generators[index].next(), index)
            except StopIteration:
                self.values[index] = None
            yield r
    

    这会将self.values 列表的元素设置为None,以指示可迭代对象已用尽。这不是处理这种极端情况的最有效方法;在version I wrote before 中,我使用字典来跟踪活动的可迭代对象,并简单地从中删除以保持索引(键)稳定。

    请注意,您可以将 t() 函数替换为内置的 iter() function

    演示:

    >>> class GeneratorSort():
    ...     def __init__(self, *args):
    ...         self.values = [(arg.next(), i) for i, arg in enumerate(args)]
    ...         self.generators = args
    ...     def generate(self):
    ...         while any(self.values):
    ...             r, index = min(v for v in self.values if v)
    ...             try:
    ...                 self.values[index] = (self.generators[index].next(), index)
    ...             except StopIteration:
    ...                 self.values[index] = None
    ...             yield r
    ... 
    >>> l1 = [2, 5, 6, 8]
    >>> l2 = [1, 4, 5, 7]
    >>> l3 = [0, 3, 9, 10]
    >>> a = GeneratorSort(iter(l1), iter(l2), iter(l3))
    >>> list(a.generate())
    [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10]
    

    标准库仍然使用heapq.merge() function 更有效地执行此操作;它使用堆以非常有效的方式保持迭代按最小值排序; min() 需要循环遍历所有 K 个可迭代对象,而使用堆只需要 log-K 步即可保持堆不变。

    >>> import heapq
    >>> list(heapq.merge(l1, l2, l3))
    [0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10]
    

    您可以研究source code,它已经过高度调整以获得最佳性能。

    【讨论】:

    • 不,用heapq.merge()代替你自己的生成器。
    【解决方案2】:

    我使用来自 Martijn Pieters 的 heapq.merge 的想法编写了这个简单的代码

    import heapq
    
    def g1():
        for i in range(0, 30, 5):
            yield i
    
    def g2():
        for i in range(15, 25, 2):
            yield i
    
    def g3():
        for i in range(5, 30, 3):
            yield i
    
    result_gen = heapq.merge(
        g1(),
        g2(),
        g3(),
    )
    
    ## convert it to list
    print list(result_gen)
    
    ## or simply iterate over it
    for x in result_gen:
        print x
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-18
      • 2013-11-03
      • 2020-10-10
      • 1970-01-01
      • 2013-01-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多