解离或重做的Python等价物?

解离或重做的Python等价物?,python,redo,Python,Redo,我正在学习Python,我有一种情况,我想使用迭代器中的项。棘手的部分是,在某些条件下,我想“取消迭代”。也就是说,在循环之前将一个项放回迭代器的前面 例如,假设我正在从树上摘苹果。我的果篮只能装10公斤,还需要倒空。但我必须先摘下每一个苹果,然后才能称重,并确定这个苹果是否会超过篮子的容量 在Perl之类的语言中,我可以unshift()将苹果放回树上,然后让循环表达式重新拾取苹果: while ($apple = shift(@tree)) { $wt = weight($apple);

我正在学习Python,我有一种情况,我想使用迭代器中的项。棘手的部分是,在某些条件下,我想“取消迭代”。也就是说,在循环之前将一个项放回迭代器的前面

例如,假设我正在从树上摘苹果。我的果篮只能装10公斤,还需要倒空。但我必须先摘下每一个苹果,然后才能称重,并确定这个苹果是否会超过篮子的容量

在Perl之类的语言中,我可以
unshift()
将苹果放回树上,然后让循环表达式重新拾取苹果:

while ($apple = shift(@tree)) {
  $wt = weight($apple);
  if ($wt + weight(@basket) > 10) {
    send(@basket);
    @basket = ();
    unshift(@tree, $apple);
  } else {
    push(@basket, $element);
  }
}
或者我也可以使用
redo
,它在块的顶部恢复处理,而不计算循环表达式。因此,同样的苹果可以在篮子清空后重新加工

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

对于这类问题,什么样的解决方案才是最有说服力的?

为什么要在else子句总是出现的时候取消移位呢

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

无论如何,我相当肯定Python没有您想要的那种行为。

在Python中,没有通用的方法将值推入迭代器。堆栈或链表更适合这种情况

如果您正在迭代列表或其他内容,当然可以手动将该项添加回列表。但是你也可以迭代那些不能以这种方式操作的对象

如果您想使用python实现该算法,那么必须选择一个允许您想要使用的操作的数据结构。我建议使用
.push()
.pop()
方法将列表视为堆栈

我正在学习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)

当我写这篇文章时,@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
虽然这不会弹出()原始迭代器中的苹果。如果这也是你想要的行为,请注意

输出

apples [1, 1, 3, 3, 1, 1, 3, 3, 2, 3]
baskets [[1, 1, 3], [3, 1, 1], [3, 3]]

您正在寻找一个生成器,一个迭代器,可以通过send()方法接收对其内部状态的修改

我会这么说。不要试图将迭代器包装在生成器表达式中,使您可以“回溯”或类似的复杂操作,而是使用while循环,就像在Perl中一样,随便你

实现的简单转换(忽略的优化):

或者,您可以使用带有有序序列索引的类
peek
功能:

while tree:
    apple = tree[0] # Take a peek at it.
    if apple.weight + basket.weight > 10:
        basket.send()
        basket.clear()
    else:
        basket.append(tree.pop(0))

如果您不喜欢“simple”参数,请查看上面(链接)线程中提到的
collections.deque
迭代器。

如果您不想遵循其他人的建议,只删除else子句,您可以编写自己的
unshift
函数,该函数将以类似于perl的方式使用任何iterable:

class UnshiftableIterable(object):
    def __init__(self, iterable):
        self._iter = iter(iterable)
        self._unshifted = [] # empty list of unshifted stuff
    def __iter__(self):
        while True:
            if self._unshifted:
                yield self._unshifted.pop()
            else:
                yield self._iter.next()
    def unshift(self, item):
        self._unshifted.append(item)
然后在代码中:

it = UnshiftableIterable(tree)
for apple in tree:
    if weigth(basket) + weight(apple) > MAX_WEIGHT:
        send(basket)
        basket = []
        it.unshift(apple)
    else:
        basket.append(apple)
对不可移动的
的一些测试:

it = UnshiftableIterable(xrange(5))

for i in it:
    print '*',
    if i == 2:
        it.unshift(10)
    else:
        print i,
# output: * 0 * 1 * * 10 * 3 * 4

顺便说一句,您真正想要的是列表。插入(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]

只是澄清一下:Python的内置迭代器没有他想要的那种行为。这似乎是最简单的解决方案。谢谢您的
UnshiftableIterator
不是迭代器(它没有
next()
方法)。它是可测试的(它有
\uu iter\uu()
方法)。@J.F.Sebastian:True。更改名称以反映这一点。
树中的苹果:
->
树中的苹果:
。否则,将永远不会使用未移位的值。感谢您提供的示例。很高兴看到如何创建一个iterable类,即使我没有在这个特殊情况下使用这个解决方案。如果$wt>10,那么就有一个无限循环(第一个示例会消耗所有内存,第二个示例永远不会停止)。@J.F:你说得对,但是在这种情况下,可以安全地假设没有一个苹果会超过10kg。谢谢,这回答了我最初提出的关于如何实现类取消移位操作的问题。感谢您提供有关send()的提示!我不确定我会在这种情况下使用它,但知道未来会很好。谢谢,记住从这个问题上退一步是很好的。与其把重点放在反移位这样的机制上,不如用一种更简单的方法来解决真正的问题。这不是反移位,而是移位。
it = UnshiftableIterable(tree)
for apple in tree:
    if weigth(basket) + weight(apple) > MAX_WEIGHT:
        send(basket)
        basket = []
        it.unshift(apple)
    else:
        basket.append(apple)
it = UnshiftableIterable(xrange(5))

for i in it:
    print '*',
    if i == 2:
        it.unshift(10)
    else:
        print i,
# output: * 0 * 1 * * 10 * 3 * 4
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