|
| 1 | +原文:[Breaking out of two loops](http://nedbatchelder.com/blog/201608/breaking_out_of_two_loops.html "Link to this post" ) |
| 2 | + |
| 3 | +--- |
| 4 | + |
| 5 | +一个常见的问题是,我如何一次性跳出两个嵌套的循环?例如,如何我才能检查字符串中的字符对,然后在我找到一对相等的字符对时停止?经典的方式是写两个嵌套循环,遍历该字符串的索引: |
| 6 | + |
| 7 | +```python |
| 8 | +s = "a string to examine" |
| 9 | +for i in range(len(s)): |
| 10 | + for j in range(i+1, len(s)): |
| 11 | + if s[i] == s[j]: |
| 12 | + answer = (i, j) |
| 13 | + break # How to break twice??? |
| 14 | +``` |
| 15 | + |
| 16 | +这里,我们使用两个循环来生成两个我们想要检查的索引。当我们找到正在寻找的条件时,我们想退出这两个循环。 |
| 17 | + |
| 18 | +有几个常见的答案。但我不大喜欢它们: |
| 19 | + |
| 20 | + * 将循环放进一个函数中,然后在函数中返回以退出循环。这个答案不尽人意,因为循环也许是重构成新函数的一个自然而然的地方,另外,也许在循环中,你需要访问其他本地变量。 |
| 21 | + * 抛出一个异常,然后在双循环的外部捕获它。这是把异常当成goto来用。这里并没有异常条件,你只是超距利用异常操作。 |
| 22 | + * 使用布尔变量来标记循环结束,然后在外部循环检查该变量,从而执行第二次退出。这是一个低技术解决方案,可能适用于某些情况,但大多数只是带来额外的噪声和标记。 |
| 23 | + |
| 24 | +我的首选答案,也就是那个我在PyCon 2013演讲上提到的([Loop Like A Native](http://nedbatchelder.com/text/iter.html))那个,是把双循环变成单循环,然后只适用一个break。 |
| 25 | + |
| 26 | +这需要把多一点的工作放到循环里,但是,这对于抽象迭代是一个好的实践。这是Python非常擅长的一件事,但也非常容易把Python当成一个能力较差的语言来用,并且不利用现有的循环抽象。 |
| 27 | + |
| 28 | +让我们再来考虑这个问题。这真的是两个循环吗?在写任何代码之前,再听一听该英文描述: |
| 29 | + |
| 30 | +> 如何我才能检查字符串中的字符对,然后在我找到一对相等的字符对时停止?(How can I examine pairs of characters in a string, stopping when I find an |
| 31 | +equal pair?) |
| 32 | + |
| 33 | +在该描述中,我并未听到两个循环。是一个字符对上的单循环。因此,让我们这样写: |
| 34 | + |
| 35 | +```python |
| 36 | +def unique_pairs(n): |
| 37 | + """Produce pairs of indexes in range(n)""" |
| 38 | + for i in range(n): |
| 39 | + for j in range(i+1, n): |
| 40 | + yield i, j |
| 41 | + |
| 42 | +s = "a string to examine" |
| 43 | +for i, j in unique_pairs(len(s)): |
| 44 | + if s[i] == s[j]: |
| 45 | + answer = (i, j) |
| 46 | + break |
| 47 | +``` |
| 48 | + |
| 49 | +这里,我们写了一个生成器来生成所需的索引对。现在,我们的循环是在字符对之上的单循环,而不是通过索引的双循环。仍然有双循环,但是抽取到unique_pairs生成器中。 |
| 50 | + |
| 51 | +这让我们的代码漂亮地匹配了我们的话。注意,我们无需写两遍len(s),这是原始代码需要重构的另一个迹象。如果在其他地方,我们发现我们想要像这样进行迭代,那么可以重用unique_pairs生成器,虽然,可重用不是写一个函数的必备条件。 |
| 52 | + |
| 53 | +我知道这种技术似乎是外来的。但它确实是最好的解决方案。如果你还是觉得被绑在双循环上,那么多想想你是如何想像你的程序的结构的。不管你相信与否,你正在试图一次打破两个循环,这意味着,在某种意义上,它们是一回事,而不是两个。将第二个隐藏到一个生成器中,你就可以以实际上认为的那样来重构你的代码。 |
| 54 | + |
| 55 | +Python有用于抽象的强大的工具,其中包括生成器和其他用于抽象迭代的技术。如果你想要了解更多的话,我的[Loop Like A Native](http://nedbatchelder.com/text/iter.html)演讲有更多详细信息(和一个令人震惊的笑话)。 |
| 56 | + |
0 commit comments