【问题标题】:Order two sorted generators without imports订购两个没有导入的排序生成器
【发布时间】:2019-12-08 06:06:19
【问题描述】:

我正在解决 stuy 的编码问题中的一个问题,并遇到了这个问题。

因此,给定两个生成器,每个生成器都以升序输出数字,将两个生成器合并为一个生成器,该生成器以升序输出数字。如果出现重复,则输出出现的次数。

我的尝试:由于我更熟悉使用列表、元组、字典等,我想我只是创建一个助手来在生成器中创建项目列表。然后我会合并这两个列表并对其进行排序

def list_maker(gener):
    l1 = []
    for item in gener:
        l1.append(item)
    return l1

def merge_gens(first_gen, second_gen):
    first_list = list_maker(first_gen)
    second_list = list_maker(second_gen)

    first_list.extend(second_list)
    final_list = first_list
    final_list.sort()

    yield from final_list

虽然这种方法似乎适用于有限生成器,但它不适用于无限生成器(我忘了说明)。我显然不能列出无限项。我可以在不导入 python 库的情况下获得有关如何执行此操作的帮助吗?

【问题讨论】:

  • 你的list-maker功能是不必要的,只需使用list(some_generator)
  • 你的任务是实现一个有效的排序。而不是你刚刚打电话给list.sort。换句话说,你没有做任何被要求的事情。阅读有关生成器和迭代器的信息,然后尝试实现它,并且只有在您尝试过某些东西时,才针对特定问题寻求帮助。
  • 我同意@zvone。鉴于我们在这里掌握的信息,我认为可以肯定地说他们不希望你只去sorted(list(gen_1)+ list(gen_2))sorted([*gen_1, *gen_2])

标签: python sorting iterator generator


【解决方案1】:

你可以试试:

def merge(first, second):
    a = next(first)
    b = next(second)
    while(True):
        # yield the smaller one
        yield a if a < b else b

        # get the next number from the
        # generator that yielded the smaller one
        if a < b:
            a = next(first)
        elif a==b:
            # when the numbers are equal
            # yield second number a second time
            yield a
            # get the next numbers from both the generators. 
            a = next(first)
            b = next(second)
        else:
            b = next(second)

很抱歉缺少 cmets 和解释。我还没有测试过边缘情况。我希望您了解该方法的一般要点,并帮助您获得进一步完成任务的指针。

假设 - StopIteration 异常将由被调用者处理

【讨论】:

  • 两件事,这不能正确处理StopIteration,并且可能会在最新版本的Python (>3.7 我相信) 上引发RuntimeError: generator raised StopIteration。其次,即使忽略这一点,它也不处理其中包含不同数量对象的生成器,它会在第一个完成后立即停止。
  • 除了@juanpa.arrivillaga 所说的,这里没有解释,没有信息。既然 OP 说他们正在解决实践问题,那么学习/教育的目标不是加倍吗?还是复制/粘贴代码?
  • 感谢@juanpa.arrivillaga 和Alexander Cécile 的发言。
  • @abhilb “假设 - StopIteration 异常将由被调用者处理”不可能。如果未处理的 StopIteration 试图从您的生成器中冒出,生成器本身将引发RuntimeError。这是对生成器行为的最新更改,以前,它们会终止您的生成器,就好像您从该生成器中引发了 StopIteration(当前仍然正确退出)
【解决方案2】:

处理边缘情况有点棘手,我玩得很开心。还没有完全测试它,现在它处于非常冗长的状态,几个帮助函数可以增加清晰度:

def merge(first, second):
    first = iter(first)
    second = iter(second)
    exhausted = object()
    f = next(first, exhausted)
    if f is exhausted:
        yield from second
    s = next(second, exhausted)
    if s is exhausted:
        yield f
        yield from first
        return
    while True:
        if f is exhausted:
            if s is not exhausted:
                yield s
            yield from second
            return
        elif s is exhausted:
            if f is not exhausted:
                yield f
            yield from first
            return
        elif f < s:
            yield f
            f = next(first, exhausted)
        elif f == s:
            yield f
            yield s
            f = next(first, exhausted)
            s = next(second, exhausted)
        else:
            yield s
            s = next(second, exhausted)

我认为通过删除一些更深层次的嵌套和重用逻辑,以下内容使其更具可读性:

def merge(first, second):
    first = iter(first)
    second = iter(second)
    exhausted = object() # just a unique sentinel value

    def _cleanup(item, iterator):
        if item is not exhausted:
           yield item
           yield from iterator

    f = next(first, exhausted)
    if f is exhausted:
        yield from second
    s = next(second, exhausted)
    if s is exhausted:
        yield from _cleanup(f, first)
        return

    while True:
        if f is exhausted:
            yield from _cleanup(s, second)
            return
        elif s is exhausted:
            yield from _cleanup(f, first)
            return
        elif f < s:
            yield f
            f = next(first, exhausted)
        elif f == s:
            yield f
            yield s
            f = next(first, exhausted)
            s = next(second, exhausted)
        else:
            yield s
            s = next(second, exhausted)

关键的想法是不断从每个迭代器请求一个值,产生最小的项目(或者如果它们相等,则产生两个项目),并且只从给你最小项目的迭代器中提取(或从两者都相等)直到一个迭代器用尽,然后通过委托给另一个迭代器来清理它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-30
    • 2011-03-31
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 2012-09-10
    • 2017-10-27
    • 2023-03-24
    相关资源
    最近更新 更多