12

I'm learning Python, and I have a situation where I want to consume items from an iterator. The tricky part is that under certain conditions, I want to "un-iterate." That is, put an item back onto the front of the iterator before I loop.

For example, suppose I'm picking apples from a tree. My fruit basket can only hold 10kg before it needs to be emptied. But I have to pick each apple before I can weigh it and determine if this apple would exceed the capacity of the basket.

In a language like Perl, I could unshift() the apple back onto the tree, and then let the loop expression re-pick the apple:

while ($apple = shift(@tree)) {
  $wt = weight($apple);
  if ($wt + weight(@basket) > 10) {
    send(@basket);
    @basket = ();
    unshift(@tree, $apple);
  } else {
    push(@basket, $element);
  }
}

Or else I can also use redo, which resumes processing at the top of block, without evaluating the loop expression. So the same apple can be re-processed, after the basket has been emptied.

while ($apple = shift(@tree)) {
  $wt = weight($apple);
  if ($wt + weight(@basket) > 10) {
    send(@basket);
    @basket = ();
    redo;
  } else {
    push(@basket, $apple);
  }
}

What would be the most pythonic solution for this kind of problem?

4

10 に答える 10

16

私は Python を学んでいて、イテレータからアイテムを消費したい状況があります。トリッキーな部分は、特定の条件下で「反復を解除」したいということです。つまり、ループする前にアイテムをイテレータの前に戻します。

簡単な解決策は次のとおりです。

class MyIterator(object):   # undo-able iterator wrapper
    def __init__(self, iterable):
        super(MyIterator, self).__init__()
        self.iterator = iter(iterable)
        self.stack = []

    def __iter__(self):
        return self

    def next(self):
        if self.stack:
            return self.stack.pop()
        return self.iterator.next()  # Raises StopIteration eventually

    def undo(self, item):
        self.stack.append(item)
for i in  MyIterator(xrange(5)): print i
0
1
2
3
4
rng = MyIterator(xrange(5))
rng.next()
0
rng.next()
1
rng.undo(1)
rng.next()
1
于 2009-01-07T02:27:28.707 に答える
13

else 句が常に発生するはずなのに、シフトを解除する必要があるのはなぜですか?

for apple in tree:
    if (apple.weight + basket.weight) > 10:
       send(basket)
       basket.clear()
    basket.add(apple)

とにかく、Python にはあなたが探しているような動作がないことは確かです。

于 2009-01-07T02:12:11.360 に答える
3

send() メソッドを介して内部状態の変更を受け取ることができるイテレータであるジェネレータを探しています

https://docs.python.org/howto/functional.html#passing-values-into-a-generator

于 2009-01-07T03:29:59.890 に答える
2

ちなみに、本当に欲しいのは list.insert(0,yourObject) です

于 2011-04-14T01:41:59.280 に答える
1

私がこの@Patrickを書いている間、すでに同じことを提案しました。しかし、私はそれを書いたので、Patrick からのコード マーキング メソッドにコメントを付けて、とにかくコードを貼り付けます。

import random

apples=[random.randint(1,3) for j in range(10)]
print 'apples',apples

basket=[]
y=6
baskets=[]

for i in range(len(apples)):
    if sum(basket+[apples[i]])>y:
        #basket is full                                                                                                                                     
        baskets.append(basket)#basket.send()                                                                                                                
        basket=[]#basket.empty()                                                                                                                            
    basket.append(apples[i])#add apple to basket                                                                                                            

print 'baskets',baskets

ただし、これは元のイテレータからリンゴを pop() しません。それも望ましい動作である場合は、コメントしてください。

出力

apples [1, 1, 3, 3, 1, 1, 3, 3, 2, 3]
baskets [[1, 1, 3], [3, 1, 1], [3, 3]]
于 2009-01-07T02:36:02.153 に答える
0

unshift の実装に関する元の質問に戻ると、operator.delitem を使用して単純な非 OO 関数を実装できます。

from operator import delitem

def unshift(l,idx):
    retval = l[0]
    delitem(l,0)
    return retval

x = [2,4,6,8]

firstval = unshift(x,0)

print firstval,x

2 [4、6、8]

于 2013-02-06T21:20:56.743 に答える
-2

Python でイテレータに値をプッシュする一般的な方法はありません。スタックまたはリンクされたリストは、それに適しています。

リストなどを繰り返し処理している場合は、もちろん、アイテムを手動でリストに追加することができます。しかし、そのような方法では操作できないオブジェクトを反復処理することもできます。

Python を使用してそのアルゴリズムを実装する場合は、使用する操作を許可するデータ構造を選択する必要があります。リストをスタックとして扱うことができる.push()andメソッドをお勧めします。.pop()

于 2009-01-07T02:18:04.250 に答える