【问题标题】:Alternating Generator交流发电机
【发布时间】:2015-07-08 00:22:46
【问题描述】:

我的教授希望我们编写一个以 *args 作为参数的生成器,并生成第一个参数的第一个值,然后是第二个参数的第一个值,然后是第三个参数的第一个值。完成后,它会产生第一个的第二个值,第二个的第二个值,等等。

我在遍历可迭代对象时遇到了一些麻烦,因为它们的长度不同。因此,虽然一个可迭代对象的长度可能为 4,但另一个可迭代对象的长度可能为 2,因此尝试产生两者的第三个值会导致错误。

我希望生成器在遇到一个已用完要迭代的值的参数时停止。

编辑:这是我目前所拥有的......

temp = list(args)
while True:
    for x in range(len(temp)):
        for letters in args:
            yield next(letters)

现在它给了我一个 Type Error: 'str' object is not an iterator 当我尝试运行这一行时...

[print(i) for i in alternate('abcde', 'fg', 'hijk')]

我不允许使用 zip 或任何其他导入。

该函数可以接受字符串和可迭代对象作为参数

【问题讨论】:

  • "我不允许使用 zip 或任何其他导入。" zip() 是内置的,而不是导入的。还禁止吗?

标签: python generator yield iterable


【解决方案1】:

Xari,你真的不需要外部 for 循环,例如 for x in range(len(temp)):

def alternate2(*args):
    if len(args) == 0:
        return
    temp = [iter(arg) for arg in args]
    while True:
        for letters in temp:
            yield next(letters)

【讨论】:

    【解决方案2】:
    def alternate(*args):
        shortest = min(map(len, args))
        for i in xrange(shortest):
            for arg in args:
                yield arg[i]
    
    for i in alternate('abcde', 'fg', 'hijk'):
        print i
    

    输出:

    a
    f
    h
    b
    g
    i
    

    【讨论】:

      【解决方案3】:

      您可以使用itertools 中的izipchain 的组合:

      from itertools import izip, chain
      
      def foo(*iters):
          return chain.from_iterable(izip(*iters))
      
      for bar in foo([1, 2], [3, 4, 5], [6, 7]):
          print bar
      

      结果:

      1
      3
      6
      2
      4
      7
      

      在此示例中,izip(*iters) 将返回一个迭代器,该迭代器生成:

      (1, 3, 6)
      (2, 4, 7)
      

      5 将不会生成,因为izip 在单个迭代器耗尽时停止。 chain.from_iterable 会将每个元组视为数字的迭代器,并将所有这些值链接在一起。

      编辑

      由于zip 是不允许的,我们可以根据itertools 页面提供的代码提供我们自己的实现:

      def foo(*iters):
          iterators = map(iter, iters)
          while iterators:
              for v in map(next, iterators):
                  yield v
      

      【讨论】:

      • 优秀的答案 :) 你赢了......不幸的是这会打破他的头......(izip_longest 可能更合适......结合filter(None,...)
      • 感谢您的回答,但不幸的是我无法在我的解决方案中使用 zip。
      • @Xari 为什么不呢? zip 是你应该怎么做的 /// (我收回我之前关于 izip 最长的声明,因为我重新阅读了 OP)
      • 我的教授希望我在不使用 zip 的情况下解决它。
      • 下次,在您的问题中提及这些要求。
      【解决方案4】:

      这是一个递归解决方案

      def myGen(x):
         for i in x:
             if len(i) < 1:
                break
             yield i[0]
         else:
            for j in myGen([z[1:] for z in x]):
                yield j
      

      【讨论】:

        【解决方案5】:

        我最终解决了这个问题(没有使用 zip,因为我不被允许)。

        temp = [iter(arg) for arg in args]
        while True:
            for x in range(len(temp)):
                for letters in temp:
                    yield next(letters)
        

        我使用列表推导来创建所有参数的列表,同时将所有参数附加为可迭代对象。之后,我只是使用了一个 while 循环来反复迭代列表中的每个值,直到我用完迭代。一旦它检测到没有更多可迭代的对象,它就会跳出循环。

        【讨论】:

        • O 现在我看到你发布了一个问题,所以你可以回答它......好吧,我 +1 这是一个非常优雅的解决方案......虽然你真的应该只迭代 temp 而不是它索引for t in temp:for letters in t:...
        • 它需要适应...如果你不提供参数它会进入一个无限循环
        【解决方案6】:

        使用zip()

        >>> def mygen(*args):
        ...     for i in zip(*args):
        ...             for j in i:
        ...                     yield j
        ...
        >>> a = mygen('abcde', 'fg', 'hijk')
        >>> next(a)
        'a'
        >>> next(a)
        'f'
        >>> next(a)
        'h'
        >>> next(a)
        'b'
        >>> next(a)
        'g'
        >>> next(a)
        'i'
        >>> next(a)
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        StopIteration
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-12
          • 1970-01-01
          • 1970-01-01
          • 2020-06-23
          相关资源
          最近更新 更多