Re: flow control and nested loops

2009-09-26 Thread Raymond Hettinger
On Sep 25, 12:01 pm, kj no.em...@please.post wrote:
 In Perl, one can label loops for finer flow control.  For example:

 X: for my $x (@X) {
   Y: for my $y (@Y) {
     for my $z (@Z) {
       next X if test1($x, $y, $z);
       next Y if test2($x, $y, $z);
       frobnicate($x, $y, $z);
     }
     glortz($x, $y);
   }
   splat($x);

 }

 What's considered best practice in the Python world for this sort
 of situation?  The only approach I can think of requires setting
 up indicator variables that must be set and tested individually;
 e.g.
 snip
 Whereas I find the Perl version reasonably readable, the Python
 one I find nearly incomprehensible.  In fact, I'm not even sure
 that the Python version faithfully replicates what the Perl one is
 doing!

 Is there a better approach?

The Perl syntax is elegant and readable.
There is not a Python direct equivalent,
but then the situation doesn't come up often.

For the outermost loop, a break or continue suffices.
To exit multiple levels of loop, there a several choices
including try/except, flags, and functions with returns.

A try/except approach looks like this:

   class NextX(Exception):pass
   class NextY(Exception):pass

   for x in X:
 try:
 for y in Y:
   try:
 for z in Z:
 if test1(x,y,z):
 raise NextX
 if test2(x,y,z):
 raise NextY
 frobnicate(x,y,z)
   except NextY: pass
 except NextX: pass


Another approach for exiting multiple levels of loops is wrap the
inner calls in a function and return from them when needed:

   def f(x):
   for y in y:
   for z in Z:
   if test1(x,y,z):
   return
   frobnicate(x,y,z)

   for x in X:
  f(x)

Or you can write a little state machine with flags and a single loop
but that isn't very readable or satisfying.


Raymond
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: flow control and nested loops

2009-09-26 Thread Bearophile
Raymond Hettinger:

 Another approach for exiting multiple levels of loops is wrap the
 inner calls in a function and return from them when needed:

    def f(x):
        for y in y:
            for z in Z:
                if test1(x,y,z):
                    return
                frobnicate(x,y,z)

    for x in X:
       f(x)

That's usual the solution I use for this problem, it's a little rough
and it has some performance cost, but it's readable and simple.

In PHP break and continue accept an optional numeric value (default is
1, 0 is of course ignored) which tells it how many nested enclosing
structures are to be broken out of.:
http://us2.php.net/manual/en/control-structures.break.php
http://us2.php.net/manual/en/control-structures.continue.php
That PHP design gives me shivers, it's horrid, bug-prone and fragile:

for x in X:
for y in Y:
for z in Z:
if test1(x, y, z):
continue 3
if test2(x, y, z):
continue 2
frobnicate(x, y, z)
glortz(x, y)
splat(x)


A better solution is to add labels to Python (I hope this code is
equivalent to the original Perl one), that can optionally be used by
continue and break. This solution design is also used by D:

label OUTER:
for x in X:
label INNER:
for y in Y:
for z in Z:
if test1(x, y, z):
continue OUTER
if test2(x, y, z):
continue INNER
frobnicate(x, y, z)
glortz(x, y)
splat(x)

Bye,
bearophile
-- 
http://mail.python.org/mailman/listinfo/python-list


flow control and nested loops

2009-09-25 Thread kj


In Perl, one can label loops for finer flow control.  For example:

X: for my $x (@X) {
  Y: for my $y (@Y) {
for my $z (@Z) {
  next X if test1($x, $y, $z);
  next Y if test2($x, $y, $z);
  frobnicate($x, $y, $z);
}
glortz($x, $y); 
  }
  splat($x); 
}

What's considered best practice in the Python world for this sort
of situation?  The only approach I can think of requires setting
up indicator variables that must be set and tested individually;
e.g.

for x in X:
next_X = False
for y in Y:
next_Y = False
for z in Z:
if test1(x, y, z):
next_X = True
break
if test2(x, y, z):
next_Y = True
break
frobnicate(x, y, z)
if next_X:
break
if next_Y:
continue
glortz(x, y) 
if next_X:
continue
splat(x) 

Whereas I find the Perl version reasonably readable, the Python
one I find nearly incomprehensible.  In fact, I'm not even sure
that the Python version faithfully replicates what the Perl one is
doing!

Is there a better approach?

TIA!

kynn

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: flow control and nested loops

2009-09-25 Thread Simon Forman
On Fri, Sep 25, 2009 at 3:01 PM, kj no.em...@please.post wrote:


 In Perl, one can label loops for finer flow control.  For example:

 X: for my $x (@X) {
  Y: for my $y (@Y) {
    for my $z (@Z) {
      next X if test1($x, $y, $z);
      next Y if test2($x, $y, $z);
      frobnicate($x, $y, $z);
    }
    glortz($x, $y);
  }
  splat($x);
 }

 What's considered best practice in the Python world for this sort
 of situation?  The only approach I can think of requires setting
 up indicator variables that must be set and tested individually;
 e.g.

 for x in X:
    next_X = False
    for y in Y:
        next_Y = False
        for z in Z:
            if test1(x, y, z):
                next_X = True
                break
            if test2(x, y, z):
                next_Y = True
                break
            frobnicate(x, y, z)
        if next_X:
            break
        if next_Y:
            continue
        glortz(x, y)
    if next_X:
        continue
    splat(x)

 Whereas I find the Perl version reasonably readable, the Python
 one I find nearly incomprehensible.  In fact, I'm not even sure
 that the Python version faithfully replicates what the Perl one is
 doing!

 Is there a better approach?

 TIA!

 kynn


snarkThe best approach would be to reorganize your code so you
didn't have to do that./snark

Seriously though, I find both the perl and python versions
non-obvious.  You have had to use constructs like this in practice?

Generally, I would use flags in tricky nested loops just like you
did, perhaps with some comments to clarify things.   An alternative
might be to use custom exceptions.  Hopefully someone smarter than me
will come along and show an even better approach.


class NextX(Exception): pass
class NextY(Exception): pass


for x in X:
try:
for y in Y:
try:
for z in Z:

if test1(x, y, z):
raise NextX

if test2(x, y, z):
raise NextY

frobnicate(x, y, z)

except NextY:
continue

glortz(x, y)

except NextX:
continue

splat(x)
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: flow control and nested loops

2009-09-25 Thread Terry Reedy

kj wrote:


In Perl, one can label loops for finer flow control.  For example:

X: for my $x (@X) {
  Y: for my $y (@Y) {
for my $z (@Z) {
  next X if test1($x, $y, $z);
  next Y if test2($x, $y, $z);
  frobnicate($x, $y, $z);
}
glortz($x, $y); 
  }
  splat($x); 
}


What's considered best practice in the Python world for this sort
of situation?  The only approach I can think of requires setting
up indicator variables that must be set and tested individually;
e.g.

for x in X:
next_X = False
for y in Y:
next_Y = False
for z in Z:
if test1(x, y, z):
next_X = True
break
if test2(x, y, z):
next_Y = True
break
frobnicate(x, y, z)
if next_X:
break
if next_Y:
continue
glortz(x, y) 
if next_X:

continue
splat(x) 


Whereas I find the Perl version reasonably readable, the Python
one I find nearly incomprehensible.  In fact, I'm not even sure
that the Python version faithfully replicates what the Perl one is
doing!

Is there a better approach?


1. Put inner loops in a function and return instead of break.

2. Put inner loops in
try:
  for..
for
  if cond: raise Something
  # or do operation that raises exception if cond is true
except e:
  whatever

tjr

--
http://mail.python.org/mailman/listinfo/python-list