Slightly OT, but I think 'break' and 'continue' should be replaced
with 'raise Break' and 'raise Continue' in Python 4000, just as we
'raise StopIteration' in generators today. This would be handy,
because you could use it in functions called by a list comprehension:
def while(x): if x > 10: raise Break else: return x
[while(x) for x in range(20)] #produces [0, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10]
Right now, you can do something this with list(generator expression)
by raising StopIteration, but it is considered a hack, and it doesn't
work with list comprehensions.
On Sat, Sep 20, 2008 at 11:41 PM, Carl Johnson carl@carlsensei.com wrote:
def while(x): if x > 10: raise Break else: return x
[while(x) for x in range(20)] #produces [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Right now, you can do something this with list(generator expression) by raising StopIteration, but it is considered a hack, and it doesn't work with list comprehensions.
I imagine what you are thinking of is:
""" def While(x): if x > 10: raise StopIteration else: return x
list(While(x) for x in range(20)) """
This doesn't seem too horrific to me, and certainly doesn't merit adding Break and Continue as Exception-like objects. I feel this would just make it unbearably hard for beginners moving in from <every other language that has looping block constructs and uses break and continue as statements>.
An alternate construct could be something like:
""" def While(x): if x > 10: return None else: return x
[w for w in (While(x) for x in range(20)) if w is not None] """
Of course, this looks silly in this example, but in a context where While were to do something nontrivial, I think it would be suitable.
-- Cheers, Leif
An alternate construct could be something like:
""" def While(x): if x > 10: return None else: return x
[w for w in (While(x) for x in range(20)) if w is not None] """
The problem is that this version won't work if range(20) is replaced
with itertools.count() or any other non-finite generator, whereas the
raise Break version will.
Another nice thing about using raise for loop control is that "raise
Continue" can be used to skip elements:
def even(x): if x % 2: raise Continue else: return x
[even(x) for x in range(10)] # [0, 2, 4, 6, 8]
This would have to be broken in two parts to do in current Python (and
indeed, that might be the chief advantage of not adopting my
proposal--TOOWTDI):
def even(x): return not x % 2
[x for x in range(10) if even(x)]
As for beginners in Python, I don't think learning to use "raise
Continue" instead of "continue" will really be that hard. I think the
main reason to reject my proposal is that complicated loops shouldn't
be done in list comprehensions at all. They should be written out,
like reduce expressions. Still, it might be convenient in some cases,
and it would be a good chance to cut out unnecessary keywords.
-- Carl
On Sat, Sep 20, 2008 at 10:06 PM, Carl Johnson carl@carlsensei.com wrote:
An alternate construct could be something like:
""" def While(x): if x > 10: return None else: return x
[w for w in (While(x) for x in range(20)) if w is not None] """
The problem is that this version won't work if range(20) is replaced with itertools.count() or any other non-finite generator, whereas the raise Break version will.
Another nice thing about using raise for loop control is that "raise Continue" can be used to skip elements:
def even(x): if x % 2: raise Continue else: return x
[even(x) for x in range(10)] # [0, 2, 4, 6, 8]
This would have to be broken in two parts to do in current Python (and indeed, that might be the chief advantage of not adopting my proposal--TOOWTDI):
def foo(): for x in range(10): if even(x): print "Even!" else: print "Odd!"
I would never expect a function could raise an exception which is silently swallowed up. Having break be a statement enforces locality, a good thing IMO.
generator expressions may currently swallow up StopIteration, but I argue this is a bug. It may not be worth fixing, but it's a bug nonetheless.
The sweet spot for list-comps and generator expressions is when they're simple. Once they start to get complicated or fancy (such as making them exit early) you should switch to an explicit for-statement; more lines really is more readable.
-- Adam Olsen, aka Rhamphoryncus
Ron Adam wrote:
Maybe break should raise a StopIteration and continue
should raise
NextIteration?
The trouble with that is that semantically, right now, StopIteration
gets caught only during the testing to see if the iteration should go
for another loop around, not during the body of the loop. Here are
some illustrations of the current behavior:
def blah(): ... yield 1 ... raise StopIteration ... yield 2 ... for x in blah(): ... print(x) ... 1
As you can see, it silently swallows the StopIteration raised
when
evaluating the iterator produced by blah().
def blah(): ... raise StopIteration ... for x in blah(): ... print("hi") ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in blah StopIteration
Above, the exception isn't swallowed, since blah() hadn't yet
been
evaluated, since it's a function, not a generator.
And if you try to use StopIteration like break, that just won't work:
for x in range(2): ... raise StopIteration ... Traceback (most recent call last): File "<stdin>", line 2, in <module> StopIteration
This is why if my proposal were accepted we would need three
loop
controlling exceptions: StopIteration, to halt the loop while fetching
the next value; ContinueIteration, to simulate the behavior of
continue; and BreakIteration, to simulate the behavior of break.
-- Carl
Carl Johnson wrote:
Slightly OT, but I think 'break' and 'continue' should be replaced with 'raise Break' and 'raise Continue' in Python 4000, just as we 'raise StopIteration' in generators today. This would be handy, because you could use it in functions called by a list comprehension:
def while(x): if x > 10: raise Break else: return x
[while(x) for x in range(20)] #produces [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Right now, you can do something this with list(generator expression) by raising StopIteration, but it is considered a hack, and it doesn't work with list comprehensions.
Maybe break should raise a StopIteration and continue should raise NextIteration?
What would be the effect on performance to for and while loops?
Ron
On Sat, Sep 20, 2008 at 9:09 PM, Ron Adam rrr@ronadam.com wrote: > >
Carl Johnson wrote: >
Slightly OT, but I think 'break' and 'continue' should be replaced with 'raise Break' and 'raise Continue' in Python 4000, just as we 'raise StopIteration' in generators today. This would be handy, because you could use it in functions called by a list comprehension:
def while(x): if x > 10: raise Break else: return x
[while(x) for x in range(20)] #produces [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Right now, you can do something this with list(generator expression) by raising StopIteration, but it is considered a hack, and it doesn't work with list comprehensions.
Maybe break should raise a StopIteration and continue should raise NextIteration?
What would be the effect on performance to for and while loops?
You would have to essentially make loops syntactic sugar for try/except statements. And if you had StopIteration and NextIteration you would have to have a nested try/except::
try: LOOP: ... loop stuff ... try: ... loop body ... except NextIteration: pass goto LOOP except StopIteration: pass
Exceptions are not expensive, but they are not exactly a no-op either.
-Brett