5

首先,我快速回顾了 c++ 风格的迭代器。例如:

//--- Iterating over vector with iterator.
vector<int> v;
. . .
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
    cout << *it << endl;
}

它是灵活的。更改底层容器类型很容易。例如,您可能稍后决定插入和删除的数量如此之多,以至于列表比向量更有效。它还有许多有用的成员函数。向量的许多成员函数都使用迭代器,例如,赋值、插入或擦除。此外,我们可以双向使用迭代器(如果支持),例如 ++、--。这对于解析类似对象的流很有用。

python的问题是: 1:目前python for循环语法不如c++ for灵活。(好吧,更安全) 2:而不是“it!= iter.end()”样式,python 将在 next() 没有更多内容时抛出异常。它不灵活。

问题1:我上面的想法正确吗?

好的。我的问题来了,如何实现一个和c++迭代器一样强大的python迭代器?目前,python for 循环语法不如 c++ for 灵活。我还找到了一些可能的解决方案,例如http://www.velocityreviews.com/forums/t684406-pushback-iterator.html。但它要求用户 push_back 一个东西,而不是问迭代器——。

问题2:在python中实现双向迭代器最好的是什么?就像http://www.cplusplus.com/reference/std/iterator/BidirectionalIterator/一样。伪代码如下:

it = v.begin();
while( it!=v.end()) {
    //do sth here

    if (condition1)
        ++it;//suppose this iterator supports ++
    if(condition2)
      --it;//suppose this iterator supports --
}

主要特点是:1)双向,2)更简单的“结束”检查。“++”或“--”运算符或常用函数无关紧要(无论如何它没有语义差异)。

谢谢,

更新:我从答案中得到了一些可能的解决方案:

i = 0
while i < len(sequence): # or i < len and some_other_condition
    star_it = sequence[i]
    if condition_one(star_it):
        i += 1
    if condition_two(star_it):
        i = max(i - 1, 0)

但是,与数组不同,列表的随机访问应该是 O(n)。我想python内部的“列表”对象是使用链表之类的东西实现的。因此,这种 while 循环解决方案效率不高。但是,在 C++ 中,我们有“随机迭代器”、“双向迭代器”。我应该如何获得更好的解决方案?谢谢。

4

5 回答 5

5

在大多数情况下,Pythonfor和迭代器是最简单的。这是他们的目标,他们不应该为了灵活性而妥协——他们缺乏灵活性不是问题

对于一些不能使用for循环的情况,C++ 迭代器可能更简单。但是总有一种方法可以在 Python 中做到这一点,它并不使用 C++ 迭代器复杂得多。


如果您需要将推进迭代器与循环分开,只需使用while循环:

it = iter(obj)

try:
    while True: # or some secondary break condition other than StopIteration
        star_it = next(it)
        if condition_one(star_it):
            star_it = next(it)
except StopIteration:
    pass # exhausted the iterator

我只能想到--it在 Python 中有意义的两种情况。

第一个是您正在迭代一个序列。在这种情况下,如果您需要倒退,根本不要使用迭代器——只需使用带有while循环的计数器:

i = 0
while i < len(sequence): # or i < len and some_other_condition
    star_it = sequence[i]
    if condition_one(star_it):
        i += 1
    if condition_two(star_it):
        i = max(i - 1, 0)

第二个是如果你正在迭代一个双向链表。在这种情况下,再次不要使用迭代器——只需正常遍历节点:

current = node
while current: # or any break condition
    if condition_one(current):
        current = current.next
    if condition_two(star_it):
        current = current.prev

可能认为这是有道理的,但您不能使用上述任何一种方法的情况是使用 a setor之类的无序集合dict。但是,在这种情况下--it 没有任何意义。由于集合是无序的,从语义上讲,以前到达的任何项目都是合适的——而不仅仅是实际的前一个项目。

因此,为了知道要返回的正确对象,您需要内存,或者通过迭代类似mydict.values()or的序列tuple(myset)并使用计数器,或者通过组装一系列先前的值并使用while循环,next而不是如上所述的一个for循环。

于 2012-04-05T13:35:39.640 回答
1

您提到的几种情况的解决方案:

  1. 您想要替换底层容器中的对象。对于字典,迭代键或项,而不仅仅是值:

    for key, value in my_dict.iteritems():
        if conditiion(value):
            my_dict[key] = new_value
    

    对于列表使用enumerate()

    for index, item in enumerate(my_list):
        if condition(item):
            my_list[index] = new_item
    
  2. 您想要一个具有一个“前瞻”值的迭代器。您可能会使用针对特定情况量身定制的东西,但这里有一个适用于一般情况的方法:

    def iter_with look_ahead(iterable, sentinel=None):
        iterable, it_ahead = itertools.tee(iterable)
        next(it_ahead, None)
        return izip_longest(iterable, it_ahead, fillvalue=sentinel)
    
    for current, look_ahead in iter_with look_ahead(tokens):
        # whatever
    
  3. 你想反向迭代。用于reversed()支持它的容器。

  4. 你想要随机访问。只需将您的可迭代对象转换为列表并使用索引:

    my_list = list(my_iterable)
    
于 2012-04-05T14:09:18.103 回答
0

Actually, C++ iterator system is not so great. Iterators are akin to pointers, and they have their woes:

  • singular values: v.end() cannot be dereferenced safely
  • inversion issues: std::for_each(end, begin, func);
  • mismatch issues: std::for_each(v0.begin(), v2.end(), func);

Python approach is much better in this regard (though the use of exception can be quite surprising at first, it really helps defining nested iterators), because contrary to its name, a Python iterator is more akin to a Range.

The concept of Range is so much better than C++11 introduces the range-for loop construct:

for (Object& o: range) {
}

Anything that is possible with an iterator is also possible with a range, though it may take some times to realize it and some translations seem surrealists at first for those of us who were educated with C++ pointer-like iterators. For example, subranges can perfectly be expressed:

for (Object& o: slice(range, 2, 9)) {
}

where slice would take all elements in position [2, 9) within range.

So, instead of fighting your language (Python) you should delve further into it and embrace its style. Fighting against a language is generally a losing battle, learn its idioms, become efficient.

于 2012-04-05T13:33:59.077 回答
0

请注意,Python 中的列表对象是一个数组,因此问题中提到的效率问题实际上不是问题。

于 2012-06-27T06:46:54.453 回答
0

您可以使用 python 对象实现类似的 C++ 方式:

class Iterable(object):
  class Iterator(object):
    def __init__(self, father, pos=0):
      self.father = father
      self.pos = pos

    def __getitem__(self, pos=0):
      return self.father[self.pos + pos]

    def __setitem__(self, pos, value):
      self.father[self.pos + pos] = value

    def __iadd__(self, increment):
      self.pos += increment
      return self

    def __isub__(self, decrement):
      self.pos -= decrement
      return self

    def __ne__(self, other):
      return self.father != other.father or self.pos != other.pos

    def __eq__(self, other):
      return not (self != other)

  def begin(self):
    return self.Iterator(self)

  def end(self):
    return self.Iterator(self, len(self))

class Vector(list, Iterable):
  pass

v = Vector([54, 43, 32, 21])

counter = 0
it = v.begin()
print it, it[0]
while it != v.end():
  counter += 1
  print it[0]
  if counter == 2:
    it += 1;  # suppose this iterator supports ++
  if counter == 1:
    it -= 1;  # suppose this iterator supports --
  it += 1

这替换*it了 by it[0](也类似于 C++)和it++by it += 1,但实际上它几乎保持不变。

但是,如果您这样做,您将离开 Pythonic 方式;-)

于 2012-04-05T13:47:45.553 回答