for: else: - any practical uses for the else clause?

P

Paul Rubin

Peter Otten said:
Such a function would have to rebind the generator:

Yeah, that's what I mean about generators not working the right way.
You can make it work, but the result tends to be messy:

I think it's better underneath, but still syntactically ugly, to just
defer the generator creation:

all_heights = lambda:
(block.height for block in stack if block.is_marked())
if is_empty(all_heights ()):
raise SomeError("No marked block")
height = sum(all_heights ())

Now sum and is_empty see two separate generators, so is_empty is
straightforward:

def is_empty(gen):
try:
gen.next()
return True
except StopIteration:
return False
 
P

Peter Otten

Paul said:
Yeah, that's what I mean about generators not working the right way.


I think it's better underneath, but still syntactically ugly, to just
defer the generator creation:

all_heights = lambda:
(block.height for block in stack if
block.is_marked())

You still need the stop() trick to omit the heights after the marked block.
if is_empty(all_heights ()):
raise SomeError("No marked block")
height = sum(all_heights ())

Now sum and is_empty see two separate generators, so is_empty is
straightforward:

def is_empty(gen):
try:
gen.next()
return True
except StopIteration:
return False

Alternatively you can turn all_heights into a list (comprehension) which
makes the test trivial.

I think it will be interesting to see how Python 3000's emphasis on
iterators will affect overall code complexity.

Peter
 
P

Paul Rubin

Peter Otten said:
You still need the stop() trick to omit the heights after the marked block.

Yeah, that code was based on my earlier mis-read of the original post.
How's this:

def blocks_until_mark():
return itertools.takewhile(lambda block:\
not block.is_marked(), \
stack)

height = sum(b.height for b in blocks_until_mark())
if is_empty(blocks_until_mark()):
raise SomeError("No marked block")
Alternatively you can turn all_heights into a list (comprehension) which
makes the test trivial.

Yes, I felt it wasn't in the spirit of the thing to use that memory.
I think it will be interesting to see how Python 3000's emphasis on
iterators will affect overall code complexity.

We will need more iterator primitives, which are still evolving.
 
C

Carl Banks

I'm wondering if anyone has ever found a practical use for the else
branch?

Say you have code that looks like this:

if command.startswith("set"):
do_set_action(command)
elif command.startswith("input"):
do_input_action(command)
elif command.startswith("print"):
do_print_action()
else:
do_general_action()

Now, during refactoring, we note that there's a lot of similarity in
all those if clauses that we can exploit to simplify the code. Let's
ignore the final else clause for now, since it's not similar to the if
and elif clauses (it has no test). We define a mapping of prefixes to
actions, and rewrite the if...elif as a for loop:

command_map = (
("set",do_set_action),
("input",do_input_action),
("print",do_print_action)
)

for keyword,action in command_map:
if command.startswith(keyword):
action(command)
break

Ok, this works great. Now let's come back to that else clause: how do
we translate default case to the for loop? Well, I guess we could set
some sort of flag indicating we got a match.... But wait! We don''t
have to! We can just leave the else clause there as-is, and it'll
work. The for loop here is quite literally a drop-in replacement for
the if...elif...elif, even going so far as to allow the same else
clause.

for keyword,action in command_list:
if command.startswith(keyword):
action(command)
break
else:
do_general_action()


Moral of the story: there are two ways to do a linear search (or linear
sequence of tests): either an unrolled sequence of if...elif...elif
clauses, or a rolled up for loop with a break. Either way you do it,
you can tack on an else clause for when you don't find anything.


Carl Banks
 
?

=?ISO-8859-1?Q?BJ=F6rn_Lindqvist?=

How do you transform this?
height = 0
for block in stack:
if block.is_marked():
print "Lowest marked block is at height", height
break
height += block.height
else:
raise SomeError("No marked block")

def get_height_of_first_marked_bock(stack):
height = 0
for block in stack:
if block.is_marked():
return height
height += block.height
raise SomeError("No marked block")

height = get_height_of_first_marked_block(stack)
print "Lowest marked block is at height", height

Yes, this transformation is one line longer, but the control flow is
much easier to understand. In general, using the for/else clause mixes
the retrieval and the usage of the element. Consider this:

for item in list:
if somecond(item):
A) do something with item
break
else:
B) exceptional code when somecond() doesnt match anything in list

The code that you write in the positions A and B really are misplaced.
They arent part of the iteration of list. The two tasks, find item and
do something with item should be separated.

def find_item(list):
for item in list:
if somecond(item):
return item
return None # OR raise an exception

item = find_item(list)
if item:
A) do something with item
else:
B) exceptional code

This is, IMHO, much better style.
 
M

MonkeeSage

BJörn Lindqvist said:
The code that you write in the positions A and B really are misplaced.
They arent part of the iteration of list. The two tasks, find item and
do something with item should be separated.

I think it is useful to have them joined. Consider a contrived example:

for i in (1,2,3,0,5,6):
try:
print 10 / i
except:
print 'Error in data'
break
else:
print 'Data processed cleanly'

Yes, you could use a flag variable instead:

done = 1
for i in (1,2,3,0,5,6):
try:
print 10 / i
except:
print 'Error in data'
done = 0
break
if done:
print 'Data processed cleanly'

....but that is not as clean imo.

Regards,
Jordan
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top