簡體   English   中英

Pythonic 相當於 unshift 或 redo?

[英]Pythonic equivalent of unshift or redo?

我正在學習 Python,我想從迭代器中使用項目。 棘手的部分是在某些條件下,我想“取消迭代”。 也就是說,在 I 循環之前將一個項目放回迭代器的前面。

例如,假設我正在從樹上摘蘋果。 我的水果籃只能裝 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);
  }
}

對於此類問題,最pythonic 的解決方案是什么?

我正在學習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

當else子句應該總是發生的時候,為什么要煩惱呢?

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

無論如何,我很確定Python沒有你想要的那種行為。

我會說最Pythonic解決方案是最簡單的解決方案 而不是試圖將迭代器包裝在允許你“回溯”或類似復雜的東西的生成器表達式中,使用while循環,就像在Perl中一樣! 迭代器不會與變異混合得非常好

簡單翻譯您的實現(忽略@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))

如果您不喜歡“簡單”參數,請查看上面(鏈接)線程中提到的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)

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

您正在尋找一個生成器,一個可以通過send()方法接收對其內部狀態的修改的迭代器

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

雖然我寫這篇文章@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]]

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

回到關於強制取消移位的原始問題,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]

目前我升級版本的pythonizer不處理redo但如果我添加它,我可能會像這樣實現它:

while (apple:=(tree.pop(0) if tree else None)):
    while True:
        wt = weight(apple)
        if wt+weight(*basket) > 10:
            sendit(basket)
            basket = []
            continue
        else:
            basket.append(apple)
        break

(注意:我必須將send更改為sendit因為send是在 perl 中預定義的。)

在python中將值推入迭代器是沒有通用的方法。 堆棧或鏈表更適合於此。

如果您正在迭代列表或某些內容,當然您可以手動將項目添加回列表。 但是,您也可以迭代無法以這種方式操作的對象。

如果要使用python實現該算法,則必須選擇允許您要使用的操作的數據結構。 我建議使用.push().pop()方法,它們可以將列表視為堆棧。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM