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など、いくつかの可能な解決策も見つけました。しかし、イテレータに尋ねるのではなく、ユーザーに何かをプッシュバックするように求めます-.

質問 2: Python で Bidirectional Iterator を実装するには何が最適ですか? 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++ イテレーターの方が簡単な場合があります。しかし、C++ 反復子を使用するよりもそれほど複雑ではない、Python でそれを行う方法が常にあります。


イテレータの進行をループから分離する必要がある場合は、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

--itPython で意味のある状況を 2 つだけ思いつきます。

1 つ目は、シーケンスを繰り返し処理していることです。その場合、逆方向に移動する必要がある場合は、イテレータをまったく使用しないで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)

2 つ目は、二重にリンクされたリストを反復処理している場合です。その場合も、反復子を使用しないでください。通常どおりノードをトラバースします。

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

理にかなっていると思うかもしれませんが、上記の方法のいずれも使用できない状況は、setまたはのような順序付けられていないコレクションdictです。ただし、その場合は意味--it がありません。コレクションは順序付けされていないため、意味的には、実際の前のアイテムだけでなく、以前に到達したアイテムのいずれかが適切です。

したがって、戻る正しいオブジェクトを知るためには、mydict.values()またはのようなシーケンスを反復処理し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. 1 つの「先読み」値を持つ反復子が必要です。おそらく特定の状況に合わせて調整されたものを使用するでしょうが、一般的な状況のレシピは次のとおりです。

    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

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

これは(C ++のアナログでもあります)とに置き換えられ*itますが、実際にはほとんど同じです。it[0]it++it += 1

ただし、これを行う場合は、Pythonの方法を残します;-)

于 2012-04-05T13:47:45.553 に答える
0

実際、C++ のイテレータ システムはそれほど優れたものではありません。イテレータはポインターに似ており、問題があります。

  • 特異値:v.end()安全に逆参照できません
  • 反転の問題:std::for_each(end, begin, func);
  • 不一致の問題:std::for_each(v0.begin(), v2.end(), func);

この点では、Python のアプローチの方がはるかに優れています (例外の使用は、最初は非常に驚くかもしれませんが、ネストされたイテレーターを定義するのに本当に役立ちます)。その名前に反して、Python イテレーターはRange.

isの概念はRange、C++11 が range-for ループ構成を導入するよりもはるかに優れています。

for (Object& o: range) {
}

イテレータで可能なことは範囲でも可能ですが、それを実現するには時間がかかる場合があり、一部の翻訳は、C++ ポインターのようなイテレータで教育を受けた私たちにとって、最初は超現実主義者のように見えます。たとえば、部分範囲は完全に表現できます。

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

where内sliceのすべての要素を配置します。[2, 9)range

したがって、自分の言語 (Python) と戦うのではなく、さらに掘り下げてそのスタイルを受け入れる必要があります。言語との戦いは一般的に負け戦であり、そのイディオムを学び、効率的になる.

于 2012-04-05T13:33:59.077 に答える
0

Python のリスト オブジェクトは配列であるため、質問で言及されている効率の問題は実際には問題ではないことに注意してください。

于 2012-06-27T06:46:54.453 に答える