2011-08-03 22 views
7

Có một số cách để thoát ra khỏi một vài vòng lồng nhauGửi StopIteration để cho vòng lặp từ bên ngoài của iterator

Họ:

1) sử dụng break-tiếp tục

for x in xrange(10): 
    for y in xrange(10): 
     print x*y 
     if x*y > 50: 
      break 
    else: 
     continue # only executed if break was not used 
    break 

2) để sử dụng trở lại

def foo(): 
    for x in range(10): 
     for y in range(10): 
      print x*y 
      if x*y > 50: 
       return 
foo() 

3) để sử dụng ngoại lệ đặc biệt

class BreakIt(Exception): pass 

try: 
    for x in range(10): 
     for y in range(10): 
      print x*y 
      if x*y > 50: 
       raise BreakIt 
except BreakIt: 
    pass 

Tôi đã nghĩ rằng có thể có một số cách khác để thực hiện điều đó. Đó là bằng cách sử dụng ngoại lệ StopIteration gửi trực tiếp đến vòng lặp bên ngoài. tôi đã viết mã này

it = iter(range(10)) 
for i in it: 
    for j in range(10): 
     if i*j == 20: 
      raise StopIteration 

Thật không may, StopIteration đã không được đánh bắt bằng cách nào cho vòng lặp và mã sản xuất một Traceback xấu xí. Tôi nghĩ rằng đó là vì StopIteration không được gửi từ bên trong của vòng lặp . (đó là dự đoán của tôi, tôi không chắc chắn về nó).

Có cách nào để tôi có thể gửi StopIteration đến vòng ngoài không?

Cảm ơn!

Trả lời

4

Bạn có thể làm một cái gì đó như thế này với coroutines:

def stoppable_iter(iterable): 
    it = iter(iterable) 
    for v in it: 
     x = yield v 
     if x: 
      yield 
      return 

Và sau đó sử dụng nó như thế này:

it = stoppable_iter(range(10)) 
for i in it: 
    for j in range(10): 
     print i, j 
     if i*j == 20: 
      it.send(StopIteration) # or any value that evaluates as True 
      break 

Và một ví dụ ngắn gọn về cách hoạt động:

>>> t = stoppable_iter(range(10)) 
>>> t.next() 
0 
>>> t.next() 
1 
>>> t.send(StopIteration) 
>>> t.next() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
StopIteration 
+0

Xin vui lòng, bạn có thể giải thích, làm thế nào soppable_iter hoạt động? Tôi không hiểu phần bắt đầu bằng nếu x: ... – ovgolovin

+0

Điều đó thực sự thông minh và ngắn hơn nhiều so với phiên bản lớp mà tôi đã cung cấp! – kindall

+0

Tôi nghĩ rằng tôi cần một cái nhìn gần hơn về coroutines, bởi vì với kiến ​​thức hiện tại của tôi về nó tôi không thể nắm bắt những gì hiện các thuật toán trong stoppable_iter. – ovgolovin

1

Tôi nghĩ rằng đó là vì StopIteration không được gửi từ bên trong của vòng lặp it. (đó là dự đoán của tôi, tôi không chắc chắn về nó).

Chính xác.

Có cách nào để tôi có thể gửi StopIteration đến vòng lặp khác không?

Tương tự như số 3 của bạn, ngoại trừ sử dụng StopIteration thay vì ngoại lệ bạn xác định. Đó là một cách tốt để sử dụng anyway.

Trong các nhận xét tôi đã đề cập bằng cách viết một trình lặp có thể được thông báo để tăng StopIteration vào lần tiếp theo thông qua vòng lặp. Dưới đây là các loại điều tôi đang nói về:

class StoppableIterator(object): 
    def __init__(self, iterable): 
     self._iter = iter(iterable) 
     self._stop = False 
    def __iter__(self): 
     return self 
    def stop(self): 
     self._stop = True 
    def next(self): 
     if self._stop: 
      raise StopIteration 
     return next(self._iter) 

Cách sử dụng:

si = StoppableIterator([2, 3, 5, 7, 11, 13]) 
for i in si: 
    for j in xrange(i): 
     print i, j 
     if j == 7: 
      si.stop() # will break out of outer loop next iteration 
      break  # breaks out of inner loop 
+0

Có thể có cách nào đó chúng ta có thể tinker với StopIteration để nó bị bắt bởi vòng lặp cần thiết? Tôi nghĩ rằng nó có thể là có thể bởi vì nếu iterator tăng StopIteration, for-loop ngoại trừ phần phải xác định từ nơi StopIteration đến từ và tuyên truyền nó nếu nó không cho nó, nhưng đối với một số vòng trên. Vì vậy, bằng cách chỉnh sửa một số tham số của đối tượng StopIteration chúng ta có thể làm cho nó bị bắt bởi vòng lặp cụ thể. – ovgolovin

+0

Không, nó không phải "xác định nó đến từ đâu" - nó chỉ đơn giản là đặt một 'try/except' xung quanh lệnh' next() ', nhận giá trị tiếp theo từ trình lặp. Nếu bắt được ngoại lệ, nó biết nó đến từ trình lặp. Bạn có thể hình dung được một iterator để tăng 'StopException' vào lần sau nó được gọi sau khi một phương thức đặc biệt được gọi, mặc dù vậy. Nó sẽ chỉ có thể thoát ra khỏi vòng lặp ở đầu trang, mặc dù. Tốt hơn là chỉ cần nâng nó lên bất cứ nơi nào bạn cần và tự mình bắt lấy nó. – kindall

+0

Oh. Bây giờ tôi nhìn thấy nó. Vì vậy, khi chúng ta viết một cái gì đó: cho i trong iterable: # do_sth for-loop trang trí phương thức next() của iterator và bắt StopIteration. Tôi không undertand làm thế nào tôi có thể viết một iterator để nâng cao StopException lần sau nó được gọi là sau khi một phương pháp đặc biệt được gọi là, chỉ cần không quản lý để có được ý tưởng. – ovgolovin

4

Một cách tiếp cận để vòng lồng nhau bạn muốn phá vỡ từ, là sụp đổ họ.Vì vậy, một cái gì đó giống như

for x, y in ((x, y) for x in range(10) for y in range(10)): 
    print x*y 
    if x*y > 50: break 
+0

Vâng. Nó thậm chí có thể được viết theo cách này: từ itertools sản phẩm nhập khẩu >>> cho i, j, k trong sản phẩm (phạm vi (5), phạm vi (6), phạm vi (7)): ... vượt qua Tôi chỉ quan tâm nếu có bất kỳ cách nào tôi có thể bắt thay đổi StopIteration để nó có thể được đánh bắt bởi đặc biệt cho vòng lặp. – ovgolovin

1

Bạn có thể sử dụng .close, mà mỗi trình tạo đều có từ Python 2.5:

Mã bằng Python 3.2, nhưng nó cũng hoạt động ở dạng 2.x.
Trong Python 2.x Tôi muốn sử dụng xrange thay vì range.

outer_loop_iterator = (i for i in range(10)) #we need named generator 
for x in outer_loop_iterator: 
    for y in range(10): 
     print(x*y) 
     if x*y > 50: 
      outer_loop_iterator.close() 
      break #I'm affraid that without this inner loop could still work 
Các vấn đề liên quan