Working around a lack of 'goto' in python

Stephen Horne steve at ninereeds.fsnet.co.uk
Mon Mar 8 00:01:04 EST 2004


On Sat, 06 Mar 2004 16:16:10 -0500, Roy Smith <roy at panix.com> wrote:

>In article <UBo2c.26879$pg4.12221 at newssvr24.news.prodigy.com>,
> "Brett" <abc at def.net> wrote:
>
>> Two areas where I've found 'goto' two be useful in other languages are in
>> (untested examples in C++)
>
>Working around the lack of "goto" in a language is kind of like trying 
>to figure out how to swim without a piano strapped to your back.

:-)

>> (1) deeply nested loops
>> 
>> for (k=0; k < 10; ++k)
>> for (j=0; j < 10; ++j)
>> for (i=0; i <10; ++i)
>>     if (/* some test */) goto END;
>> 
>> END: /* continue */;
>
>I've found two general solutions to the breaking out of a deeply nested 
>loop.  One possibility is to factor the loop out into its own function, 
>and break out of it with a return:

<snip>

>Another is to use an exception:

Another occurred to me. Turn the three nested loops into one loop.

In C...

  i = j = k = 0;

  while (k < 10)
  {
    if (/* some test */)  break;

    i++; if (i == 10) { i = 0; j++; if (j == 10) { j = 0; k++; } }
  }

If it was needed a lot, I might try...

  /*  Cyclic increment

      Increment var reference by p and test against limit.
      if limit reached, reset to zero and return true
      to indicate overflow
  */
  int CInc (int *p, int p_Limit)
  {
    (*p)++;
    if (*p < p_Limit)  return 0;
    *p = 0;
    return true;
  }

  ...

  i = j = k = done = 0;

  while (!done)
  {
    if (/* some test */)  break;

    done = CInc (&i, 10) && CInc (&j, 10) && CInc (&k, 10);
  }

though I would at least try to think of a decent name for that
function.


This can just about be translated to Python...

  class cyclicint :
    def __init__ (self, p) :
      self.value = p

    def inc (self, p_Limit) :
      self.value++
      if self.value < p_Limit : return false
      self.value = 0
      return true

    def __int__ (self) :
      return value

  ...

  i = cyclicint (0); j = cyclicint (0); k = cyclicint (0); done = 0

  while not done :
    if some test :  break

    done = i.inc (10) and j.inc (10) and k.inc (10)


But really that's a bad idea - there's a much neater way...

  for (i,j,k) in [(x,y,z) for x in range(10)
                          for y in range(10)
                          for z in range(10) ] :
    if sometest : break


This should, of course, get a bit more efficient in Python 2.4 when we
get generator expressions :-)

I can also imagine a helper function...

  for (i,j,k) in cross(range(10), range(10), range(10)) :
    if sometest : break


-- 
Steve Horne

steve at ninereeds dot fsnet dot co dot uk



More information about the Python-list mailing list