2009-01-07 58 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]