[Python-checkins] r56131 - peps/trunk/pep-0000.txt peps/trunk/pep-3136.txt

georg.brandl python-checkins at python.org
Sat Jun 30 20:31:14 CEST 2007


Author: georg.brandl
Date: Sat Jun 30 20:31:14 2007
New Revision: 56131

Added:
   peps/trunk/pep-3136.txt
Modified:
   peps/trunk/pep-0000.txt
Log:
Add PEP 3136 by Matt Chisholm, ``Labeled break and continue''.


Modified: peps/trunk/pep-0000.txt
==============================================================================
--- peps/trunk/pep-0000.txt	(original)
+++ peps/trunk/pep-0000.txt	Sat Jun 30 20:31:14 2007
@@ -103,6 +103,7 @@
  S  3124  Overloading, Generic Functions, Interfaces   Eby
  S  3134  Exception Chaining and Embedded Tracebacks   Yee
  S  3135  New Super                                    Spealman, Delaney
+ S  3136  Labeled break and continue                   Chisholm
  S  3141  A Type Hierarchy for Numbers                 Yasskin
 
  Finished PEPs (done, implemented in Subversion)
@@ -505,6 +506,7 @@
  SR 3133  Introducing Roles                            Winter
  S  3134  Exception Chaining and Embedded Tracebacks   Yee
  S  3135  New Super                                    Spealman, Delaney
+ S  3136  Labeled break and continue                   Chisholm
  S  3141  A Type Hierarchy for Numbers                 Yasskin
 
 
@@ -541,6 +543,7 @@
     Cannon, Brett            brett at python.org
     Carlson, Josiah          jcarlson at uci.edu
     Carroll, W Isaac         icarroll at pobox.com
+    Chisholm, Matt           matt-python at theory.org
     Coghlan, Nick            ncoghlan at gmail.com
     Cole, Dave               djc at object-craft.com.au
     Craig, Christopher       python-pep at ccraig.org

Added: peps/trunk/pep-3136.txt
==============================================================================
--- (empty file)
+++ peps/trunk/pep-3136.txt	Sat Jun 30 20:31:14 2007
@@ -0,0 +1,485 @@
+PEP: 3136
+Title: Labeled break and continue
+Version: $Revision$
+Last-Modified: $Date$
+Author: Matt Chisholm <matt-python at theory.org>
+Status: Draft
+Type: Standards Track
+Content-Type: text/x-rst
+Created: 30-Jun-2007
+Python-Version: 3.1
+Post-History:
+
+
+Abstract
+========
+
+This PEP proposes support for labels in Python's ``break`` and
+``continue`` statements.  It is inspired by labeled ``break`` and
+``continue`` in other languages, and the author's own infrequent but
+persistent need for such a feature.
+
+
+Introduction
+============
+
+The ``break`` statement allows the programmer to terminate a loop
+early, and the ``continue`` statement allows the programmer to move to
+the next iteration of a loop early.  In Python currently, ``break``
+and ``continue`` can apply only to the innermost enclosing loop.
+
+Adding support for labels to the ``break`` and ``continue`` statements
+is a logical extension to the existing behavior of the ``break`` and
+``continue`` statements.  Labeled ``break`` and ``continue`` can
+improve the readability and flexibility of complex code which uses
+nested loops.
+
+For brevity's sake, the examples and discussion in this PEP usually
+refers to the ``break`` statement.  However, all of the examples and
+motivations apply equally to labeled ``continue``.
+
+
+Motivation
+==========
+
+If the programmer wishes to move to the next iteration of an outer
+enclosing loop, or terminate multiple loops at once, he or she has a
+few less-than elegant options.
+
+Here's one common way of imitating labeled ``break`` in Python (For
+this and future examples, ``...`` denotes an arbitrary number of
+intervening lines of code)::
+
+    for a in a_list:
+        time_to_break_out_of_a = False
+        ...
+        for b in b_list:
+            ...
+            if condition_one(a, b):
+                break
+            ...
+            if condition_two(a, b):
+                time_to_break_out_of_a = True
+                break
+            ...
+        if time_to_break_out_of_a:
+            break
+        ...
+
+
+This requires five lines and an extra variable,
+``time_to_break_out_of_a``, to keep track of when to break out of the
+outer (a) loop.  And those five lines are spread across many lines of
+code, making the control flow difficult to understand.
+
+This technique is also error-prone.  A programmer modifying this code
+might inadvertently put new code after the end of the inner (b) loop
+but before the test for ``time_to_break_out_of_a``, instead of after
+the test. This means that code which should have been skipped by
+breaking out of the outer loop gets executed incorrectly.
+
+This could also be written with an exception.  The programmer would
+declare a special exception, wrap the inner loop in a try, and catch
+the exception and break when you see it::
+
+    class BreakOutOfALoop(Exception): pass
+
+    for a in a_list:
+        ...
+        try:
+            for b in b_list:
+                ...
+                if condition_one(a, b):
+                    break
+                ...
+                if condition_two(a, b):
+                    raise BreakOutOfALoop
+                ...
+        except BreakOutOfALoop:
+            break
+        ...
+
+
+Again, though; this requires five lines and a new, single-purpose
+exception class (instead of a new variable), and spreads basic control
+flow out over many lines.  And it breaks out of the inner loop with
+``break`` and out of the other loop with an exception, which is
+inelegant. [#toowtdi]_
+
+This next strategy might be the most elegant solution, assuming
+condition_two() is inexpensive to compute::
+
+    for a in a_list:
+        ...
+        for b in b_list:
+            ...
+            if condition_one(a, b):
+                break
+            ...
+            if condition_two(a, b):
+                break
+            ...
+        if condition_two(a, b)
+            break
+        ...
+
+
+Breaking twice is still inelegant.  This implementation also relies on
+the fact that the inner (b) loop bleeds b into the outer for loop,
+which (although explicitly supported) is both surprising to novices,
+and in my opinion counter-intuitive and poor practice.
+
+The programmer must also still remember to put in both breaks on
+condition two and not insert code before the second break.  A single
+conceptual action, breaking out of both loops on condition_two(),
+requires four lines of code at two indentation levels, possibly
+separated by many intervening lines at the end of the inner (b) loop.
+
+
+Other languages
+---------------
+
+Now, put aside whatever dislike you may have for other programming
+languages, and consider the syntax of labeled ``break`` and
+``continue``.  In Perl::
+
+    ALOOP: foreach $a (@a_array){
+        ...
+        BLOOP: foreach $b (@b_array){
+            ...
+            if (condition_one($a,$b)){
+                last BLOOP; # same as plain old last;
+            }
+            ...
+            if (condition_two($a,$b)){
+                last ALOOP;
+            }
+            ...
+        }
+        ...
+    }
+
+
+(Notes: Perl uses ``last`` instead of ``break``.  The BLOOP labels
+could be omitted; ``last`` and ``continue`` apply to the innermost
+loop by default.)
+
+PHP uses a number denoting the number of loops to break out of, rather
+than a label::
+
+    foreach ($a_array as $a){
+        ....
+        foreach ($b_array as $b){
+            ....
+            if (condition_one($a, $b)){
+                break 1;  # same as plain old break
+            }
+            ....
+            if (condition_two($a, $b)){
+                break 2;
+            }
+            ....
+	}
+        ...
+    }
+
+
+C/C++, Java, and Ruby all have similar constructions.
+
+The control flow regarding when to break out of the outer (a) loop is
+fully encapsulated in the ``break`` statement which gets executed when
+the break condition is satisfied.  The depth of the break statement
+does not matter.  Control flow is not spread out.  No extra variables,
+exceptions, or re-checking or storing of control conditions is
+required.  There is no danger that code will get inadvertently
+inserted after the end of the inner (b) loop and before the break
+condition is re-checked inside the outer (a) loop.  These are the
+benefits that labeled ``break`` and ``continue`` would bring to
+Python.
+
+
+What this PEP is not
+====================
+
+This PEP is not a proposal to add GOTO to Python.  GOTO allows a
+programmer to jump to an arbitrary block or line of code, and
+generally makes control flow more difficult to follow.  Although
+``break`` and ``continue`` (with or without support for labels) can be
+considered a type of GOTO, it is much more restricted.  Another Python
+construct, ``yield``, could also be considered a form of GOTO -- an
+even less restrictive one.  The goal of this PEP is to propose an
+extension to the existing control flow tools ``break`` and
+``continue``, to make control flow easier to understand, not more
+difficult.
+
+Labeled ``break`` and ``continue`` cannot transfer control to another
+function or method.  They cannot even transfer control to an arbitrary
+line of code in the current scope.  Currently, they can only affect
+the behavior of a loop, and are quite different and much more
+restricted than GOTO.  This extension allows them to affect any
+enclosing loop in the current name-space, but it does not change their
+behavior to that of GOTO.
+
+
+Specification
+=============
+
+Under all of these proposals, ``break`` and ``continue`` by themselves
+will continue to behave as they currently do, applying to the
+innermost loop by default.
+
+
+Proposal A - Explicit labels
+----------------------------
+
+The for and while loop syntax will be followed by an optional ``as``
+or ``label`` (contextual) keyword [#keyword]_ and then an identifier,
+which may be used to identify the loop out of which to break (or which
+should be continued).
+
+The ``break`` (and ``continue``) statements will be followed by an
+optional identifier that refers to the loop out of which to break (or
+which should be continued).  Here is an example using the ``as``
+keyword::
+
+    for a in a_list as a_loop:
+        ...
+        for b in b_list as b_loop:
+            ...
+            if condition_one(a, b):
+                break b_loop  # same as plain old break
+            ...
+            if condition_two(a, b):
+                break a_loop
+            ...
+        ...
+
+Or, with ``label`` instead of ``as``::
+
+    for a in a_list label a_loop:
+        ...
+        for b in b_list label b_loop:
+            ...
+            if condition_one(a, b):
+                break b_loop  # same as plain old break
+            ...
+            if condition_two(a, b):
+                break a_loop
+            ...
+        ...
+    
+
+This has all the benefits outlined above.  It requires modifications
+to the language syntax: the syntax of ``break`` and ``continue``
+syntax statements and for and while statements.  It requires either a
+new conditional keyword ``label`` or an extension to the conditional
+keyword ``as``. [#as]_ It is unlikely to require any changes to
+existing Python programs.  Passing an identifier not defined in the
+local scope to ``break`` or ``continue`` would raise a NameError.
+
+
+Proposal B - Numeric break & continue
+-------------------------------------
+
+Rather than altering the syntax of ``for`` and ``while`` loops,
+``break`` and ``continue`` would take a numeric argument denoting the
+enclosing loop which is being controlled, similar to PHP.
+
+It seems more Pythonic to me for ``break`` and ``continue`` to refer
+to loops indexing from zero, as opposed to indexing from one as PHP
+does.
+
+::
+
+    for a in a_list:
+        ...
+        for b in b_list:
+            ...
+            if condition_one(a,b):
+                break 0  # same as plain old break
+            ...
+            if condition_two(a,b):
+                break 1
+            ...
+        ...
+
+Passing a number that was too large, or less than zero, or non-integer
+to ``break`` or ``continue`` would (probably) raise an IndexError.
+
+This proposal would not require any changes to existing Python
+programs.
+
+
+Proposal C - The reduplicative method
+-------------------------------------
+
+The syntax of ``break`` and ``continue`` would be altered to allow
+multiple ``break`` and continue statements on the same line.  Thus,
+``break break`` would break out of the first and second enclosing
+loops.
+
+::
+
+    for a in a_list:
+        ...
+        for b in b_list:
+            ...
+            if condition_one(a,b):
+                break  # plain old break
+            ...
+            if condition_two(a,b):
+                break break
+            ...
+        ...
+
+
+This would also allow the programmer to break out of the inner loop
+and continue the next outermost simply by writing ``break continue``,
+[#breakcontinue]_ and so on.  I'm not sure what exception would be
+raised if the programmer used more ``break`` or ``continue``
+statements than existing loops (perhaps a SyntaxError?).
+
+I expect this proposal to get rejected because it will be judged too
+difficult to understand.
+
+This proposal would not require any changes to existing Python
+programs.
+
+
+Proposal D - Explicit iterators
+-------------------------------
+
+Rather than embellishing for and while loop syntax with labels, the
+programmer wishing to use labeled breaks would be required to create
+the iterator explicitly and assign it to a identifier if he or she
+wanted to ``break`` out of or ``continue`` that loop from within a
+deeper loop.
+
+::
+
+    a_iter = iter(a_list)
+    for a in a_iter:
+        ...
+        b_iter = iter(b_list)
+        for b in b_iter:
+            ...
+            if condition_one(a,b):
+                break b_iter  # same as plain old break
+            ...
+            if condition_two(a,b):
+                break a_iter
+            ...
+        ...
+
+
+Passing a non-iterator object to ``break`` or ``continue`` would raise
+a TypeError; and a nonexistent identifier would raise a NameError.
+This proposal requires only one extra line to create a labeled loop,
+and no extra lines to break out of a containing loop, and no changes
+to existing Python programs.
+
+
+Proposal E - Explicit iterators and iterator methods
+----------------------------------------------------
+
+This is a variant of Proposal D.  Iterators would need be created
+explicitly if anything other that the most basic use of ``break`` and
+``continue`` was required.  Instead of modifying the syntax of
+``break`` and ``continue``, ``.break()`` and ``.continue()`` methods
+could be added to the Iterator type.
+
+::
+
+    a_iter = iter(a_list)
+    for a in a_iter:
+        ...
+        b_iter = iter(b_list)
+        for b in b_iter:
+            ...
+            if condition_one(a,b):
+                b_iter.break()  # same as plain old break
+            ...
+            if condition_two(a,b):
+                a_iter.break()
+            ...
+        ...
+
+
+I expect that this proposal will get rejected on the grounds of sheer
+ugliness.  However, it requires no changes to the language syntax
+whatsoever, nor does it require any changes to existing Python
+programs.
+
+
+Implementation
+==============
+
+I have never looked at the Python language implementation itself, so I
+have no idea how difficult this would be to implement.  If this PEP is
+accepted, but no one is available to write the feature, I will try to
+implement it myself.
+
+
+Footnotes
+=========
+
+.. [#toowtdi] Breaking some loops with exceptions is inelegant because
+   it's a violation of There's Only One Way To Do It.
+
+.. [#keyword] Or really any new contextual keyword that the community
+   likes: ``as``, ``label``, ``labeled``, ``loop``, ``name``, ``named``,
+   ``walrus``, whatever.
+
+.. [#as] The use of ``as`` in a similar context has been proposed here, 
+   http://sourceforge.net/tracker/index.php?func=detail&aid=1714448&group_id=5470&atid=355470
+   but to my knowledge this idea has not been written up as a PEP. 
+
+.. [#breakcontinue] To continue the Nth outer loop, you would write
+   break N-1 times and then continue.  Only one ``continue`` would be
+   allowed, and only at the end of a sequence of breaks. ``continue
+   break`` or ``continue continue`` makes no sense.
+
+
+Resources
+=========
+
+This issue has come up before, although it has never been resolved, to
+my knowledge.
+
+* `labeled breaks`__, on comp.lang.python, in the context of
+  ``do...while`` loops
+
+  __ http://groups.google.com/group/comp.lang.python/browse_thread/thread/6da848f762c9cf58/979ca3cd42633b52?lnk=gst&q=labeled+break&rnum=3#979ca3cd42633b52
+  
+* `break LABEL vs. exceptions + PROPOSAL`__, on python-list, as
+  compared to using Exceptions for flow control
+
+  __ http://mail.python.org/pipermail/python-list/1999-September/#11080
+
+* `Named code blocks`__ on python-list, a suggestion motivated by the
+  desire for labeled break / continue
+
+  __ http://mail.python.org/pipermail/python-list/2001-April/#78439
+
+* `mod_python bug fix`__ An example of someone setting a flag inside
+  an inner loop that triggers a continue in the containing loop, to
+  work around the absence of labeled break and continue
+
+  __ http://mail-archives.apache.org/mod_mbox/httpd-python-cvs/200511.mbox/%3C20051112204322.4010.qmail@minotaur.apache.org%3E
+
+
+Copyright
+=========
+
+This document has been placed in the public domain.
+
+
+
+..
+   Local Variables:
+   mode: indented-text
+   indent-tabs-mode: nil
+   sentence-end-double-space: t
+   fill-column: 70
+   coding: utf-8
+   End:


More information about the Python-checkins mailing list