[英]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.