Python 迭代器小节--与C++迭代器的一些思考

最近花了20天才把C++ Primer的“容器与算法”一章看完,过程太长,严重打乱了我的读书计划,但是看的时间长有时间长的好处,我可以不断地反思C++容器与算法设计的优劣。结合前一段时间看的《Python cookbook》一书中的迭代器,特做此札记。

1、Python iter()

先说python中的迭代器,特点:简单易用,方便自定义,还有强大的itertools模块增强迭代器的功能。

iter(arg)以可迭代对象arg作为形参生成一个迭代器对象,所谓的迭代器对象其实是实现了python迭代器协议的对象。Python的迭代器协议是指__iter__() 方法返回一个实现了 __next__() 方法的迭代器对象,通过next()方法遍历这个迭代器对象,最终以StopIteration停止遍历。

如:

class Node:
    def __init__(self, value):
        self._value = value
        self._children = []

    def __repr__(self):
        return 'Node({!r})'.format(self._value)

    def add_child(self, node):
        self._children.append(node)

    def __iter__(self):
        return iter(self._children)

这里iter(self._children)只是简单地调用self._children.__iter__()返回对应的迭代器对象。这是一种较为简单的传递迭代请求的操作。

如果想创建新的迭代模式(不同于range()、reversed()函数的操作),可以借助python的生成器来实现:

def frange(start, stop, increment):
    x = start
    while x < stop:
        yield x
        x += increment

你可以用for循环迭代它或者使用其他接受一个可迭代对象的函数(比如 sum() , list() 等)。

>>> for n in frange(0, 4, 0.5):
    ... print(n)

>>> 0
0.5
1.0
1.5
2.0
2.5
3.0
3.5
>>> list(frange(0, 1, 0.125))
>>> [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875]

for循环会自动处理迭代终止等细节。

reversed()实现反向迭代:

>>> a = [1, 2, 3, 4]
>>> for x in reversed(a):
...     print(x)
>>> 4
3
2
1

前面看到的迭代器都是简单的前向迭代(forward iterator),而python中也有实现了类似于C++中的reverse_iterator的功能。程序员可以在自定义类上实现 __reversed__() 方法来实现反向迭代。

class Countdown:
    def __init__(self, start):
        self.start = start

    # Forward iterator
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

    # Reverse iterator
    def __reversed__(self):
        n = 1
        while n <= self.start:
            yield n
            n += 1

for rr in reversed(Countdown(30)):
    print(rr)
for rr in Countdown(30):
    print(rr)

Python 迭代器的缺陷在于并不能对其进行随机访问,但是itertools模块实现了类似于C++随机访问迭代器(random-access iterator)的功能:

>>> def count(n):
...     while True:
...         yield n
...         n += 1
...
>>> c = count(0)
>>> c[10:20]
Traceback (most recent call last):
    File "", line 1, in 
TypeError: 'generator' object is not subscriptable

>>> # Now using islice()
>>> import itertools
>>> for x in itertools.islice(c, 10, 20):
...     print(x)

函数 islice() 返回一个可以生成指定元素的迭代器,它通过遍历并丢弃直到切片开始索引位置的所有元素。 然后才开始一个个的返回元素,并直到切片结束索引位置。

同时,itertools模块还提供了对输入序列的排列、组合形式的迭代:itertools.permutations(),itertools.combinations();对多个序列的迭代:itertools.chain()操作等。这些API可以极大地减少冗余代码并且提高程序运行效率。

2、C++的迭代器

C++的迭代器与标准库std中的容器和算法是密不可分的,标准库的100多个泛型算法是根据迭代器设计的,而容器的大部分操作采用迭代器来实现。同时,有根据容器支持的操作来给迭代器进行分类。

标准库根据所支持的操作定义了五种迭代器类别:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。

输入迭代器是只能读不能写元素的迭代器,python中的迭代器都是输入迭代器。C++中的输入迭代器有istream_iterator类型,从输入流istream对象中读取数据,这种迭代器支持 == 、!= 、++ 、解引用操作符*和箭头操作符->,迭代器只能向前递进指向下一个元素。

输出迭代器可以向容器写入元素,如ostream_iterator类型,向输入流ostream对象写入数据。

前向迭代器只会以一个方向遍历序列,可以读写序列。

双向迭代器可以从两个方向读写迭代器,即支持 ++ 和 -- 操作,支持这种类型迭代器的容器有 list、map、set。需要使用双向迭代器的泛型算法有reverse()。

随机访问迭代器提供在常量时间内访问容器任意位置的功能,它除了支持双向迭代器的所有功能外,还支持迭代器与整形数值n之间的加减法操作、两个迭代器之间的减法操作、下表操作符iter[n](等同于*(iter+n))。需要用到随机访问迭代器的算法有sort()函数。vector、deque、string的迭代器都是随机访问迭代器,内置元素数组的指针也是随机访问迭代器。

 

总结python和C++中的迭代器知识,私以为还是python更胜一筹,结合了生成器的迭代器无论是在效率上还是代码简洁之美上都比C++要好一点。C++标准库算法基于迭代器进行设计,而由于各种顺序容器、关联容器支持的迭代操作不同,标准库算法并不能完全适用所有的序列类型,因此泛型算法中存在很多重载版本和不同命名规则版本的算法,这给阅读维护代码库都带来了负担。

以上是小生个人理解,如有错误,望诸君指出!

你可能感兴趣的:(C++,Python)