2009-01-07 36 views
12

Tôi đang học Python, và tôi có một tình huống mà tôi muốn tiêu thụ các mục từ một trình lặp. Phần khó khăn là trong những điều kiện nhất định, tôi muốn "không lặp lại". Tức là, đặt một mục trở lại mặt trước của trình lặp trước khi tôi lặp lại.Pythonic tương đương với unshift hoặc redo?

Ví dụ: giả sử tôi chọn táo từ cây. Giỏ trái cây của tôi chỉ có thể chứa 10kg trước khi nó cần phải được làm trống. Nhưng tôi phải chọn từng quả táo trước khi tôi có thể cân nhắc và xác định xem quả táo này có vượt quá khả năng của giỏ hay không.

Trong một ngôn ngữ như Perl, tôi có thể unshift() táo trở lại vào cây, và sau đó cho phép sự biểu hiện vòng lặp lại chọn táo:

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

Hoặc khác tôi cũng có thể sử dụng redo, mà lại tiếp tục chế biến ở đầu khối, mà không đánh giá biểu thức vòng lặp. Vì vậy, cùng một quả táo có thể được tái chế, sau khi giỏ đã được làm trống.

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

Điều gì sẽ là nhất giải pháp pythonic cho loại vấn đề?

+0

nếu $ wt> 10 thì có vòng lặp vô hạn (ví dụ đầu tiên ăn hết bộ nhớ, thẻ thứ hai không bao giờ dừng). – jfs

+0

@ J.F .: Bạn nói đúng, nhưng trong trường hợp này, an toàn để giả sử không có quả táo nào vượt quá 10kg. –

Trả lời

13

Tại sao phải thay đổi khi mệnh đề khác luôn xảy ra?

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

Dù sao, tôi khá chắc chắn rằng Python không có loại hành vi bạn đang tìm kiếm.

+1

Chỉ cần làm rõ: Các trình lặp lặp nội tuyến * của Python * không có loại hành vi mà anh ta đang tìm kiếm. – cdleary

+0

Tôi đã thay đổi kiểu mã. Vui lòng quay lại. – jfs

+0

Điều này dường như là giải pháp đơn giản nhất. Cảm ơn! –

-2

Không có cách nào chung để đẩy giá trị vào một trình lặp trong python. Một ngăn xếp hoặc danh sách liên kết phù hợp hơn với điều đó.

Nếu bạn đang lặp qua danh sách hoặc thứ gì đó, tất nhiên bạn có thể thêm mục theo cách thủ công trở lại danh sách. Nhưng bạn cũng có thể lặp qua các đối tượng không thể thao tác theo cách như vậy.

Nếu bạn muốn sử dụng python để thực hiện thuật toán đó, bạn sẽ phải chọn cấu trúc dữ liệu cho phép các thao tác bạn muốn sử dụng. Tôi đề xuất các phương pháp .push().pop() cho phép bạn coi danh sách là ngăn xếp.

16

Tôi đang học Python và tôi có một tình huống mà tôi muốn sử dụng các mục từ trình lặp. Phần khó khăn là trong những điều kiện nhất định, tôi muốn "không lặp lại". Tức là, đặt một mục trở lại mặt trước của trình lặp trước khi tôi lặp lại.

Dưới đây là một giải pháp đơn giản:

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

Cảm ơn, điều này trả lời câu hỏi ban đầu của tôi về cách người ta có thể thực hiện một hoạt động giống như unshift. –

1

Trong khi tôi đang viết @Patrick này đã đề nghị điều tương tự. Nhưng kể từ khi tôi đã viết nó, tôi sẽ dán mã anyways, với ý kiến ​​trong các phương pháp đánh dấu mã từ 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 

mặc dù điều này không bật() táo từ trình lặp ban đầu. Xin vui lòng nhận xét nếu đó là một hành vi mong muốn quá.

đầu ra

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

Cảm ơn ví dụ! –

3

Bạn đang tìm kiếm một máy phát điện, một iterator có thể nhận được những thay đổi trạng thái nội bộ của mình thông qua phương thức gửi()

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

+0

Cảm ơn bạn đã gửi mẹo này về send()! Tôi không chắc mình sẽ dùng nó trong trường hợp này, nhưng thật tuyệt khi biết về tương lai. –

6

Tôi muốn nói rằng the most Pythonic solution is the simplest one. Thay vì cố gắng bọc một trình lặp trong một biểu thức máy phát điện cho phép bạn "quay lại" hoặc một cái gì đó tương tự phức tạp, hãy sử dụng một vòng lặp while, như bạn có trong Perl! Iterators don't mix very nicely with mutation, anywho.

Simple dịch thực hiện của bạn (bỏ qua @Patrick 's tối ưu hóa):

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) 

Hoặc, bạn có thể sử dụng một chức năng -like peek với chỉ số thứ tự ra lệnh:

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)) 

Nếu bạn don' t như đối số "đơn giản", hãy xem các trình vòng lặp collections.deque được đề cập trong chuỗi (liên kết) ở trên.

+1

Xin cảm ơn, bạn nên nhớ lùi bước từ vấn đề. Thay vì tập trung vào một cơ chế như unshift, cách tốt nhất là giải quyết vấn đề thực sự một cách đơn giản hơn. –

4

Nếu bạn không muốn làm theo gợi ý của người kia chỉ loại bỏ các khoản khác, bạn có thể viết unshift chức năng của riêng bạn mà sẽ làm việc theo một cách tương tự như của perl với bất kỳ 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) 

Sau đó, trong mã của bạn:

it = UnshiftableIterable(tree) 
for apple in tree: 
    if weigth(basket) + weight(apple) > MAX_WEIGHT: 
     send(basket) 
     basket = [] 
     it.unshift(apple) 
    else: 
     basket.append(apple) 

Một số thử nghiệm của 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

'UnshiftableIterator' của bạn không phải là một trình lặp (nó không có phương thức' next() '). Nó có thể lặp lại (nó có phương thức '__iter __()'). – jfs

+0

@ J.F.Sebastian: Đúng vậy. Thay đổi tên để phản ánh điều đó. – nosklo

+0

'cho táo trong cây:' -> 'cho táo trong đó:'. Nếu không thì giá trị chưa được thay đổi sẽ không bao giờ được sử dụng. – jfs

0

Bằng cách này, những gì bạn thực sự muốn là list.insert (0, yourObject)

0

Quay lại câu hỏi ban đầu về impementing unshift, operator.delitem thể được sử dụng để thực hiện một chức năng đơn giản-OO phi:

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]

+0

Đó không phải là unshift - đó là sự thay đổi. –

Các vấn đề liên quan