for-else

B

bearophileHUGS

So far in Python I've almost hated the 'else' of the 'for' loops:
- I have problems to remember its meaning;
- It gives me little problems when I later want to translate Python
code to other languages (and you always have to translate long-lived
code).
- I have used it only once, so far.

So so far I'd liked to see it removed from Python 3.0.

But then this article:
http://tratt.net/laurie/tech_articles/articles/the_high_risk_of_novel_language_features
has shown me that my problems with the 'else' of the 'for' mostly come
from just its bad naming. The converge language is yet another very
Python-like language, and it uses a better naming (the word
"exhausted" is long and has a complex spelling for non-English
speakers, so it's not perfect):

for ...:
...
exhausted:
...
broken:
...

The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").

Bye,
bearophile
 
C

Carl Banks

So far in Python I've almost hated the 'else' of the 'for' loops:
- I have problems to remember its meaning;
- It gives me little problems when I later want to translate Python
code to other languages (and you always have to translate long-lived
code).
- I have used it only once, so far.

So so far I'd liked to see it removed from Python 3.0.

But then this article:http://tratt.net/laurie/tech_articles/articles/the_high_risk_of_novel...
has shown me that my problems with the 'else' of the 'for' mostly come
from just its bad naming. The converge language is yet another very
Python-like language, and it uses a better naming (the word
"exhausted" is long and has a complex spelling for non-English
speakers, so it's not perfect):

for ...:
...
exhausted:
...
broken:
...

The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").


I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.

For instance, if you have a (trivial) if...elif...else like this:

if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()

(Please never mind the trivialness of this example; I know you can
eliminate the for loop altogether; this is JUST an example.)

I keep this analogy in mind when using for...else to keep the
semantics straight.


Carl Banks
 
B

BJörn Lindqvist

for ...:
...
exhausted:
...
broken:
...

The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").


I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.

For instance, if you have a (trivial) if...elif...else like this:

if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()

You forgot the break statement. The else suite will always be executed
in this loop. Kind of proves bearophiles point, for-else is really
tricky.
 
T

Tim Chase

For instance, if you have a (trivial) if...elif...else like this:
if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()

important "break" missing here...
else:
do_default_task()


or otherwise this code will do_task_i *and* do_default_task()...

-tkc
 
C

Carl Banks

I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.
For instance, if you have a (trivial) if...elif...else like this:
if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()
You could roll it up into a for...else statement like this:
for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()

You forgot the break statement. The else suite will always be executed
in this loop. Kind of proves bearophiles point, for-else is really
tricky.

Ah ha, but that would have been a mistake with or without the else
clause....


Carl Banks
 
S

Shane Geiger

if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

The if-elif-else structure that calls functions (like that above) can be
avoided with the code below:


def foo0(): print 'foo0'
def bar0(): print 'bar0'
def foo1(): print 'foo1'
def bar1(): print 'bar1'
def do_default_task(): print 'do_default_task'

do_task = { 0:foo0, 1:foo1, 2:bar0, 3:bar1, }

a = 1

# example of normal usage
if a in do_task.keys(): do_task[a]()
else: do_default_task()

# example of testing all functions in the dict as well as the default
function
for a in do_task.keys() + [8]: # 8 is a non-existent key in the do_task
dict
print "a is ",a,"and it gives this output:",
if a in do_task.keys(): do_task[a]()
else: do_default_task()





Carl said:
for ...:
...
exhausted:
...
broken:
...

The meaning is explicit. While "else" seems to mean little there.
So I may like something similar for Python 3.x (or the removal of the
"else").

I would not be opposed to this on its own merits, but there is a
rationale behind the name "else". If you consider a for loop to be a
rolled-up if...elif...else statement (situations where this is
reasonable tend to be the same ones were else would be useful), then
the "else" clause would remain unchanged on the for loop.

For instance, if you have a (trivial) if...elif...else like this:

if a == 0:
do_task_0()
elif a == 1:
do_task_1()
elif a == 2:
do_task_2()
else:
do_default_task()

You could roll it up into a for...else statement like this:

for i in range(3):
if a == i:
do_task[a]()
else:
do_default_task()
You forgot the break statement. The else suite will always be executed
in this loop. Kind of proves bearophiles point, for-else is really
tricky.

Ah ha, but that would have been a mistake with or without the else
clause....


Carl Banks

This approach works well for me:



def foo0(): print 'foo0'
def bar0(): print 'bar0'
def foo1(): print 'foo1'
def bar1(): print 'bar1'

def do_default_task(): print 'do_default_task'

do_task = { 0:foo0, 1:foo1, 2:bar0, 3:bar1, }

a = 1

# example of normal usage
if a in do_task.keys(): do_task[a]()
else: do_default_task()


# example of testing
for i in range(len(do_task.keys)):
if a in do_task.keys(): do_task[a]()
else: do_default_task()



--
Shane Geiger
IT Director
National Council on Economic Education
(e-mail address removed) | 402-438-8958 | http://www.ncee.net

Leading the Campaign for Economic and Financial Literacy
 
C

castironpi

Would you like it to be removed or its name changed?

You can do it with a special iteration:

for a in B:
if behavior
break
else:
2behavior

---->

class KeepResult:...
kr= KeepResult( B )
for a in kr:
if behavior
break
if kr.diditbreak?:
2behavior
(if not:
3behavior)

It just can't do automatic continues; you'd need a
kr.i'mcontinuingonthisone(). Would that be useful in addition?
 
J

Jeffrey Froman

Carl said:
there is a
rationale behind the name "else".  If you consider a for loop to be a
rolled-up if...elif...else statement

This is an interesting angle. I've always considered "for/else" to be
unintuitive, not because of "else", but because of the coupling with "for".
Instead, I think of this as a "break/else" statement, and everything gels
for me. The same applies to my comprehension of "while/else".

I wonder how this came to be called "for/else" or "for-else". I haven't
spotted that expression in the python docs yet.

With whatever name, I find the construct quite elegant and regularly useful.


Jeffrey
 
R

Raymond Hettinger

[BearOphile]
So far in Python I've almost hated the 'else' of the 'for' loops

FWIW, I'm very happy with for-else. Most of the time, you don't need
it, but when you do, it beats the heck out of doing silly tricks with
flags.

The primary use case is searching a container:

prep_tasks()
for item in container:
if predicate(item):
found_tasks()
break
else:
not_found_tasks()
follow_up_tasks

Raymond
 
B

bearophileHUGS

Raymond HettInger:
FWIW, I'm very happy with for-else. Most of the time, you don't need
it, but when you do, it beats the heck out of doing silly tricks with
flags.

I'd like it to be renamed to something more natural :)

Bye,
bearophile
 
T

Terry Reedy

| So far in Python I've almost hated the 'else' of the 'for' loops:
| - I have problems to remember its meaning;

Consider the following pseudoPython which you should understand:

label: loop
if cond:
do_something()
goto loop
else:
do_else()

We actually write the above as

while cond:
do_something()
else:
do_else()

Same meaning; do_else is executed when condition is false.

A for-loop is equivalent to a while loop with the condition 'iterator is
not exhausted'. So do_else when that condition is false -- the iterator is
exhausted.

Terry Jan Reedy
 
B

Ben Finney

But then this article:
http://tratt.net/laurie/tech_articles/articles/the_high_risk_of_novel_language_features
has shown me that my problems with the 'else' of the 'for' mostly
come from just its bad naming. The converge language is yet another
very Python-like language, and it uses a better naming (the word
"exhausted" is long and has a complex spelling for non-English
speakers, so it's not perfect):

for ...:
...
exhausted:
...
broken:
...

Rather than adding new keywords, I think the above would be better
spelled using the exception keywords::

for foo in bar_sequence:
# normal iteration
spam(foo)
if funky(foo):
break
except StopIteration, exc:
# the iterator stopped normally
eggs(exc)
else:
# the iterator exited abnormally, i.e. 'break'
sausage()
finally:
# always executed, even on 'break'
beans()
 
C

castironpi

That you could do yourself, CMIIW correct me if I'm wrong.

try:
    for foo in iterex( bar_sequence ):
        # normal iteration
        spam(foo)
        if funky(foo):
            break
    except StopIterationEx, exc:
        # the iterator stopped normally
        eggs(exc)
    else:
        # the iterator exited abnormally, i.e. 'break'
        sausage()
    finally:
        # always executed, even on 'break'
        beans()

Qualm, except would be needed in every for-, and in one sense doesn't
obey the semantics of exceptions. The end of a finite list is not an
exceptional element-- it's not one. However generator semantics don't
yield finite sequences-- and finity is an exception, but for-loops
don't use infinite ones.

Makes it sound (*subj've) like I'm hacking by using for-loops..... or,
like sequencetype.__iter__ is malformed. <staggered diminished chord>
 
T

Troels Thomsen

The primary use case is searching a container:

prep_tasks()
for item in container:
if predicate(item):
found_tasks()
break
else:
not_found_tasks()
follow_up_tasks

I've found myself mimicing this again and again in c, and was pleased to
find it in python and use it regularely.
int i
for (i = 0 ; i < 10 ; ++i)
blah
if i == 10
not_found_tasks()

The discussion of words is silly. My surprise about "else following a for
loop.... what the heck ...." lasted excactly as long as it takes to read
this sentence.


tpt
 
B

bearophileHUGS

I've found myself mimicing this again and again in c, and was pleased to
find it in python and use it regularely.
int i
for (i = 0 ; i < 10 ; ++i)
blah
if i == 10
not_found_tasks()

The discussion of words is silly. My surprise about "else following a for
loop.... what the heck ...." lasted excactly as long as it takes to read
this sentence.

tpt
 
B

bearophileHUGS

Troels Thomsen:
The discussion of words is silly. My surprise about "else following a for
loop.... what the heck ...." lasted excactly as long as it takes to read
this sentence.

Maybe I don't follow what you are saying, but well chosen words are a
very important part of a well designed API. If you take a look at the
Python developers mailing list you may see that people discuss days,
weeks to find the best naming for things.

Bye,
bearophile
 
C

castironpi

Troels Thomsen:


Maybe I don't follow what you are saying, but well chosen words are a
very important part of a well designed API. If you take a look at the
Python developers mailing list you may see that people discuss days,
weeks to find the best naming for things.

They should've called it "or-else". Bor hor hor. ...The keyword, I
mean.
 
J

Jeffrey Barish

Terry said:
A for-loop is equivalent to a while loop with the condition 'iterator is
not exhausted'.  So do_else when that condition is false -- the iterator
is exhausted.

I think that this is the most important statement in this thread. As others
have expressed, I too found for-else surprising when I first encountered
it. It made sense to me when I analogized for with if:

for x in range(5):
do_something()
else:
do_something_else()

means

do_something repeatedly when the condition (iterator not exhausted) is true
and do_something_else when the condition is not true, just as

if condition:
do_something()
else:
do_something_else()

means do_something once when the condition is true and do_something_else
when the condition is not true. I find it elegant that Python does not
introduce additional keywords to deal with situations that are comparable.
 
S

Sion Arrowsmith

Jeffrey Barish said:
Terry said:
A for-loop is equivalent to a while loop with the condition 'iterator is
not exhausted'.  So do_else when that condition is false -- the iterator
is exhausted.
I think that this is the most important statement in this thread. As others
have expressed, I too found for-else surprising when I first encountered
it. It made sense to me when I analogized for with if: [ ... ]

And to pull those two together, I found while-else comprehensible by
analogy with if-else:

while stmt:
do_something() # stmt is True
else:
do_something_else() # stmt is False

I don't think I've ever used a for-else in the wild, but I have used
while-else. And knowing how that works, it's kind of obvious what
for-else does.
 
E

egbert

The idea of the if-else is:
.. depending on some condition either do this or do something else,
.. don't do them both.

If you approach a loop-else with this same idea, you get:
.. depending on the loop conditions either do the loop,
.. or do something else, but not both.

However the loop-else really works more like this:
.. try to do the loop;
.. if it starts but is interrupted by a break,
.. then do something else as well.

So they are completely different beasts, and if you try to use
or explain the one according to the rules of the other one,
you put a serious strain on your synapses.

The explanation that the if-else and the loop-else
follow the same pattern, runs more or less like this:
.. all conditions to run the loop to its completion were met,
.. which means that the loop-condition is not met (any more),
.. which means that we must do something else.
For me that is orwellian logic: success is failure.

The loop-else mechanism is not an easy one:
earlier in this thread Carl Banks turned it into an either/or construct
by omitting the crucial break; Jeffrey Barish initially found
the loop-else surprising; Sion Arrowsmith found it comprehensible
only after some hard thinking (as in the last paragraph).

The culprit is of course the keyword, else.
The idea of an easy follow-up after a loop is a good one.
But we need other keywords, I think at least three of them:
.. skipped - the loop did not run at all
.. broken - the loop was interrupted (by a break)
.. exhausted - the loop ran to completion.
The last two as suggested by Laurence Tratt in his Convergence.
e
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,269
Messages
2,571,338
Members
48,028
Latest member
chasetony

Latest Threads

Top