Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

comment on

( [id://3333]=superdoc: print w/replies, xml ) Need Help??

Regarding:

LINE: { # ... redo LINE if # ... # ... }
you might as well drop the block and s/redo/goto/. I'm not a fanatic about "no gotos", but I do hate to see people making very silly "loops" in order write goto-ish code without having to mention the much-maligned operator.

But the original code has a pretty silly loop as well. If you are going to use a loop, then have the body of the loop be the code that actually gets looped over. In two of three code examples, the bottom of the loop is never looped over and so it should just be taken out of the loop! (Using the conditional at the top of a "loop" to skip over the loop and a bunch of never-looped end code doesn't make the code easier to understand.)

In bikeNomad's last example, there is a "next if" at the bottom of the loop so that should just be moved into the loop conditional (and doing so is trivial). One of the points of loops in structured programming is so that the reasons that you will stay in (or leave) the loop are put in one location so that you don't have to reanalyze the entire code of the loop in order to figure it out.

So the heart of bikeNomad's last example can be rewritten as:

my $done = 0; # ... sub { my( @data, $row, $key ); my $found= 0; while( ! $found && ! $done && defined( $row= <$fh> ) ) { chomp($row); ( $found, $done )= predicate( $row, \$key, \@data ); } return( $key, \@data ) if $allow; return;
Note that you can now have predicate() return (1,1) to say that we are done after you return the current set.

I think that one reason that people often don't do this is that they feel restricted on how much code that should put between the parens of "while()". This example didn't demonstrate the problems of a huge conditional very well, but there are a couple of tricks that can aleviate those types of problems.

One is just to write your code like this:

while( conditional ) { code }
then you can have more than one line of coditionals and still have a clean look.

Another is to use either:

for( init; condition; cont; ) { code; }
or:
init; while( contition ) { code; } continue { cont; }
and I prefer the former for simple cases (all the information about the loop is in one central place) and the latter for complex cases (the code flows in the direction that it is typed).

Note that the "continue" block is only useful if you use next inside of "code". If properly and very sparingly used, next, redo, and last can be better than goto. But they can easily become worse than goto because at least goto make it pretty obvious where you are going to. With the other three, you have to parse the surrounding code looking for enclosing blocks, find the top (and sometimes even the bottom) of each block to determine if it is a "loop" block or not, etc. until you find the enclosing loop and then (except in the case of redo) find the end of that loop (to check for a continue, in the case of next). I know because I've seen horrible spaghetti code that never mentioned goto.

For a bigger challenge, let's redo the original code:

my $done= 0; # ... my( @data, $row, $key ); return if $done || ! defined( $row= <$fh> ); do { chomp( $row ); # [SNIPPED] munge $row, set $key and @data } while( $key < $min && defined( $row= <$fh> ) ); if( $max < $key ) { $done = 1; return; } return( $key, \@data ); }
and you see I've duplicated defined( $row= <$fh> ). In general, duplicating code is something to avoid. But such a small bit of code is worth duplicating for the much greater clarity of the loop. If the code was more complicated than that, it probably deserves to be rolled up into an interator of its own: $fh->readline(\$row).

So we end up choosing between the following evils:

  • Duplicating code
  • Duplicating tests
  • Putting code inside tests
  • Putting conditional branching in the middle of loops
and if you can't eliminate most of these, it usually means you need to move some code into a separate subroutine.

        - tye (but my friends call me "Tye")

In reply to (tye)Re: Closures as Itterators by tye
in thread Closures as Itterators by blakem

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (3)
As of 2024-04-25 21:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found