Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic equivalent of unshift or redo?

Tags:

python

redo

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?

like image 278
Bill Karwin Avatar asked Jan 07 '09 01:01

Bill Karwin


2 Answers

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.

Here's a simple solution:

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
like image 86
Federico A. Ramponi Avatar answered Sep 26 '22 03:09

Federico A. Ramponi


Why bother with unshifting when the else clause should always occur?

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

Anyway, I'm fairly certain that Python doesn't have the sort of behavior you're looking for.

like image 20
Patrick Avatar answered Sep 25 '22 03:09

Patrick