[Python-ideas] Loop labels
Terry Reedy
tjreedy at udel.edu
Fri Mar 9 19:16:24 CET 2012
On 3/9/2012 3:22 AM, Matt Joiner wrote:
> Yeah it's definitely non trivial. Reading this is what got me thinking
> about it in Python: mortoray.com/2011/10/23/the-ideal-language-has-goto/
> <http://mortoray.com/2011/10/23/the-ideal-language-has-goto/>
I disagree as most would understand the claim. Let us consider his first
example:
location_t where;
for( int i=0; i < num_rows; ++i )
{
for( int j=0; j < num_cols; ++j )
{
if( matrix(i,j).is_what_we_want() )
{
where.set(i,j);
goto found;
}
}
}
throw error( "not-found" );
found:
//do something with it
Let us leave aside the fact that searching the matrix should normally be
factored out as a function or method unto itself, separate from code
that uses the found object. A Python solution is to use the implied goto
of try/except/else and make 'where' an exception:
class where(Exception):
def __init__(self, i, j):
self.i = i
self.j = j
try:
for i in range(num_rows):
for j in range(num_cols):
if matrix(i,j).is_what_we_want():
raise where(i,j)
raise ValueError('matrix does not have what we want')
except where as w:
do_something(w)
If one wants to reuse the app specific exception, replace 'raise
ValueError' with the following at the end.
else:
raise where(None,None)
Either way, this is as least as clean and clear as the goto example. One
advantage is that the jump information object is created and initialized
only when and where needed. A virtue of try: is that it tells the reader
that there is going to be some jumpy control-flow. People too often
think that exceptions are errors or only for errors. There are not
(which is why those that *are* are named SomethingError). They are jump
objects that carry information with the jump. Also exception gotos obey
the restriction of only going up the stack. So Python already has a
version of what the Mortoray advocates. (And hence, in a way, I agree
with the claim ;-).
The redo example can be done similarly. There are two types of jumps.
class Redo(Exception):
"Restart the loop process from the top"
class Break(Exception):
"We are completely done with the loop process"
while True:
try:
<nested loops>
if must_redo_from_top: raise Redo()
<more code>
if completely_done: raise Break
except Redo: pass
except Break: break
The error-handling example is easily done with a Broken exception.
If performance is an issue for a state machine, it is best written in C.
If the machine is very much more complex than the example, it is best
generated programmatically from higher-level specs. Code in the example
is the sort of spaghetti code that become unreadable and unmaintainable
with very many more states and input conditions. (I am old enough to
have tried -- and sometimes given up -- working with such code.)
Even in C, it is often better, when possible, to use tables to map state
and input to action, output, and next state. That, of course, can be
done just as well (though slower) in Python. In fact, it may be easier
in Python if the table is sparse. The result can be structurally simple
code like
<populate table or tables>
current_state = initial_state
for item in input_stream:
action, output, current_state = table[current_state][item]
# or table[current_state].get(item,default) for sparse rows
# replace 'item' with 'group(item)' to classify inputs
perform(action)
print(output)
if current_state == exit_state:
return
--
Terry Jan Reedy
More information about the Python-ideas
mailing list