迭代器和生成器有什么区别?

35

迭代器(Iterator)和生成器(Generator)的区别是什么?


2
我为自己丢失了 Griswold 关于 Icon 编程语言的书感到后悔。据我所知,那是迭代器和生成器首次作为语言特性出现的地方,而且解释非常好。当然,那是20多年前的事情,我认为它们永远不会出现在我实际用于生产的任何语言中。但现在它们已经出现在 Python 中,并即将出现在 JavaScript 中,所以我想我错了。 - Nosredna
1
类似于 Python 生成器和迭代器的区别,但是针对 Python 特定的内容。 - kenorb
10个回答

46

迭代器 逐个遍历集合中的元素。

生成器 逐个生成序列中的元素。

例如,您可以迭代生成器的结果...


39

生成器是迭代器,但并非所有迭代器都是生成器。

迭代器通常是具有next方法以从流中获取下一个元素的对象。生成器是与函数相关联的迭代器。

例如在Python中的生成器:

def genCountingNumbers():
  n = 0
  while True:
    yield n
    n = n + 1

这样做的好处是您无需将无限数量的数值存储在内存中以便迭代。

您可以像使用任何迭代器一样使用它:

for i in genCountingNumbers():
  print i
  if i > 20: break  # Avoid infinite loop

您还可以遍历数组:

for i in ['a', 'b', 'c']:
  print i

10

这里有太多的Python,太多人说生成器是实现无限迭代器的唯一方法。这是我提到的例子(所有自然数的平方)在C#中的实现。 ExplicitSquares 显式地实现了一个迭代器(在C#中称为 IEnumerator)。 ImplicitSquares 使用生成器完成相同的操作。两者都是无限迭代器,并没有后台集合。 唯一的区别是状态机是否被明确说明,或者使用生成器。

using System.Collections;
using System.Collections.Generic;
using System;

class ExplicitSquares : IEnumerable<int>
{
    private class ExplicitSquaresEnumerator : IEnumerator<int>
    {
        private int counter = 0;

        public void Reset()
        {
            counter = 0;
        }

        public int Current { get { return counter * counter; }}

        public bool MoveNext()
        {
            counter++;
            return true;
        }

        object IEnumerator.Current { get { return Current; } }

        public void Dispose(){}
    }

    public IEnumerator<int> GetEnumerator()
    {
        return new ExplicitSquaresEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

class ImplicitSquares : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        int counter = 1;
        while(true)
        {
            int square = counter * counter;
            yield return square;
            counter++;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class AllSquares
{
    private static readonly int MAX = 10;

    public static void Main()
    {
        int i = 0;
        foreach(int square in new ExplicitSquares())
        {
            i++;
            if(i >= MAX)
                break;
            Console.WriteLine(square);
        }

        Console.WriteLine();

        int j = 0;
        foreach(int square in new ImplicitSquares())
        {
            j++;
            if(j >= MAX)
                break;
            Console.WriteLine(square);
        }
    }
}

5

生成器是迭代器的一种实现。它通常是一个例程,向其调用者产生多个值,而不仅仅是一个。

在C#中

// yield-example.cs
using System;
using System.Collections;
public class List
{
    public static IEnumerable Power(int number, int exponent)
    {
        int counter = 0;
        int result = 1;
        while (counter++ < exponent)
       {
            result = result * number;
            yield return result;
    }
}

static void Main()
{
    // Display powers of 2 up to the exponent 8:
    foreach (int i in Power(2, 8))
    {
        Console.Write("{0} ", i);
    }
}
}

查看维基百科的条目


5
一个生成器是一个特殊的函数,可以作为迭代器的行为,每次调用时返回一个值。因为它是一个函数,所以它可以按需计算每个值。而且因为它是特殊的,它可以记住上次被调用时的状态,所以生成的代码看起来非常简单。
例如,在 Python 中,这个生成器将产生一系列整数。
def integers():
    int n = 0
    while True:
        yield n
        n += 1

在这个例子中,最重要的是 yield n 语句。函数将返回该值,并在下一次调用时从该点继续执行。
此链接有关于Python生成器的更详细解释:链接文本

4

1
迭代器用于遍历集合中的对象,无论是数组、链表、树、哈希映射还是其他类型的集合。您有一堆对象,并希望对它们中的每一个执行某些操作。
生成器不仅返回有限对象集合中的项,而是在运行时动态生成它们。您可以将其概念化为在迭代过程中创建的集合上的迭代器,该集合可能没有有限大小。
例如,您可以拥有从2到无穷大生成素数的生成器。您无法拥有“所有质数”的集合并使用迭代器进行迭代。您需要一个生成器。
或者,您可以拥有一个生成器,该生成器接受一个整数并逐个生成该数字的因子。在这里,生成器会为您提供帮助,因为您可以逐个检查因子,而无需为所有因子预分配内存。它还允许您在生成因子时立即使用它们,而不必事先生成整个列表,这可能比您想要的慢。以下是Python中此类生成器的示例:
def factors(n):
    for i in xrange(1, n+1):
        if n % i == 0:
            yield i

for n in factors(1234567890):
    print n

如果您运行此代码,您将会看到因子在计算时被打印出来。我们不需要实际上在内存中维护所有因子的完整列表。

1
再说一遍,这是错误的。迭代器不必具有“真实”的后备集合(数组、链表或其他)。 - Matthew Flaschen

1
通常,迭代器遍历现有序列(如数组或列表),而生成器在每次请求时计算新值。

这不是正确的。可以(无需生成器)制作一个迭代器,例如提供每个自然数的平方。没有现有的数组或列表对象支持它。 - Matthew Flaschen
如果你把那个称作迭代器,那么迭代器和生成器之间的区别是什么呢? - John Kugelman
区别基本上就是未知(谷歌)所说的。 “生成器是绑定到函数的迭代器”。当然,“函数”实际上是一个看起来像函数的状态机。我在答案中提供了一个示例。 - Matthew Flaschen

0

易于理解的定义如下:

  • 迭代器是用于迭代一系列项目的对象。

  • 生成器是生成迭代器的函数。

  • 生成器迭代器是由生成器生成的迭代器。


流行但令人困惑的定义如下:

  • 迭代器是一个遍历项目列表的对象。

  • 生成器函数是一个生成迭代器的函数。

  • 生成器是由生成器函数生成的迭代器。


0

迭代器通常用于遍历一组项目。通常具有MoveNext()和Current()方法。 MoveNext()将把指针移动到下一个集合项(如果可能),并根据成功与否返回true/false。 Current()将提供实际值。

生成器是迭代器的一种实现,但它不是指向预先存在的集合,而是在每个MoveNext()调用上创建新项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接