2009-01-07 43 views
12

我正在学习Python,并且我有一种情况需要使用迭代器中的项目。棘手的部分是,在某些条件下,我想“不重复”。也就是说,在我循环之前,将一个项目放回到迭代器的前面。pythonic相当于不换或重做?

例如,假设我正在从树上摘苹果。需要清空前,我的水果篮只能装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); 
    } 
} 

什么是对这类问题的最Python的解决方案?

+0

如果$重量> 10则是一个无限循环(第一示例吃的所有存储器,第二个刚刚从不停止)。 – jfs 2009-01-08 20:48:44

+0

@ J.F .:你是对的,但在这种情况下,假设没有一个苹果会超过10公斤是安全的。 – 2009-01-08 22:46:30

回答

13

当else子句总是发生时,为什么还要解决这个问题?

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

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

+1

只是为了澄清:Python的* builtin迭代器*没有他正在寻找的那种行为。 – cdleary 2009-01-07 19:45:37

+0

我改变了代码风格。随时回滚。 – jfs 2009-01-07 20:47:56

+0

这似乎是最直接的解决方案。谢谢! – 2009-01-10 02:26:30

-2

有没有办法将值推入python中的迭代器。堆栈或链接列表更适合于此。

如果您正在迭代列表或其他东西,当然您可以手动添加项目回到列表。但是你也可以迭代不能以这种方式操作的对象。

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

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 
+0

谢谢,这回答了我原来的问题,关于如何实现一个不移位操作。 – 2009-01-10 02:27:27

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 

虽然这不会从原始迭代器中弹出()苹果。请注意,如果这也是一种理想的行为。

输出

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

感谢您的示例! – 2009-01-10 02:31:46

6

我说那the most Pythonic solution is the simplest one。不要试图将一个迭代器包装在一个生成器表达式中,该生成器表达式允许您“回溯”或类似复杂的东西,请使用while循环,就像在Perl中一样! Iterators don't mix very nicely with mutation,anywho。你的实现

简单的翻译(忽略@Patrick的优化):

while tree: 
    apple = tree.pop(0) 
    if apple.weight + basket.weight > 10: 
     basket.send() 
     basket.clear() 
     tree.insert(0, apple) # Put it back. 
    else: 
     basket.append(apple) 

或者,你可以使用一个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)) 

“ t像“简单”的参数,请查看上面(链接)线程中提到的迭代器collections.deque

4

如果你不想遵循的只是去掉else子句对方的建议,你可以写自己的unshift功能将类似于Perl的任何可迭代的方式工作:

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) 

UnshiftableIterable的一些测试:

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

顺便说一句,你真正想要的是list.insert(0,yourObject)

0

返回至impementing不印字原来的问题,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]