kvale has asked for the wisdom of the Perl Monks concerning the following question:

It occured to me that in my years of programming perl, I had only used goto once (to break out of a deep loop) and have never used redo. It is generally agreed that minimzing the number of goto statements helps in understanding the control flow of a program. But am I missing something with redo?

perlfunc says whimsically that redo should be used when the program wants to lie to itself. That quip and the associated code snippet did not seem very compelling. But Larry, may he live forever, must have seen a clear need for redo.

What uses do you find for redo in your code?

-Mark

Replies are listed 'Best First'.
Re: Useful uses of redo?
by dragonchild (Archbishop) on Aug 26, 2004 at 18:16 UTC
    Input validation is an excellent place.
    my %is_legal = map { $_ => 1 } qw( y yes n no ); my $answer; { print "What is your answer? "; chomp( $answer = <> ); $answer = lc $answer; last if $is_legal{ $answer }; redo; } # Do something with $answer, which is now guaranteed to be 'y', 'yes', + 'n', or 'no'

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

      Hmm, I always use until() for that, since the test fails on a null value you enter the loop.

      --
      I'm not belgian but I play one on TV.

Re: Useful uses of redo?
by tilly (Archbishop) on Aug 26, 2004 at 19:21 UTC
    For breaking out of a deep loop in Perl I would suggest using last and named loops instead of goto. I've only seen 2 necessary uses of the traditional goto in Perl, and that is not one of them.

    For redo, look at the implementation of Carp::Heavy in the standard distribution. You could write long_error_loc and short_error_loc without using redo, but I think that it would not read as naturally.

Insert new items into foreach list:
by ikegami (Patriarch) on Aug 26, 2004 at 18:20 UTC

    I used it for the first time yesterday to pseudo-insert new items into my foreach list:

    sub line_wrap { my ($str, $max) = @_; my $line; my @lines; foreach $line (split($BRK, $str)) { # # Collapse spaces and tabs. # s/\s+/ /g; if (length($line) > $max) { my $max2 = $max - 1; if ($line =~ s/^(.{0,$max2}\S)\s+//s) { # Break at space. push(@lines, $1); } else { # No space. $line =~ s/^(.{0,$max})//s; push(@lines, $1); } # Check if remainder needs to be broken. redo; } else { # No need to break. push(@lines, $line); } } return join("\n", @lines) . "\n"; }

    Of course, I could very simply rewrite the 'if' as a 'while' to eliminate the 'redo'.

    In short
    redo unless ...;
    replaces a nested loop, possibly making it easier to read.

Re: Useful uses of redo?
by BrowserUk (Patriarch) on Aug 26, 2004 at 18:32 UTC

    Apparently, if your used to using do/while loops in p5, your gonna have to replace them with some hooky construction of

    1. A bare block.
    2. A last if condition
    3. And a redo

    in P6!?

    Which seems like a completely retrograde step to me. Apparently this is because "people often use do/while wrongly...". By my reckoning, if we applied that logic to the rest of perl, we'd be left with scalar assignment and if/goto. Hey! Fortran IV.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

      I guess you can do:

      { # do { stmt; stmt; stmt; redo if $cond; # } while ($cond); }

      and

      { # repeat { stmt; stmt; stmt; redo unless $cond; # } until ($cond); }

      but why? yuck! It's like saying that while loops while henceforth be like:

      { last unless $cond; # while ($cond) stmt; stmt; stmt; redo; # wend }
        do { } still exists. It's just the statement-modifier while-on-do that is taken away.
      In Perl 6 that is done by
      loop { # do something last if $done; }

        Okay, we've traded a simple, extensible, easily used and self-documenting construct for YAKW and less self documenting code.

        I often use

        ... my( $p, $i ) = 0; $p += $i while $i = 1+index $haystack, $needle, $i; ...

        Sometimes, when debugging I need to see what is going on, so I wrap the code and add some debug:

        ... my( $p, $i ) = 0; do{ $p += $i; print "$p : $i, other stuff"; } while $i = 1+index $haystack, $needle, $i; ...

        Once I'm done debugging, I can just comment out the print lines and I am back to where I was.

        In p6, I will have to completely change the coding of the single line with modifier version of the loop to an alien and less clear construct when I need to debug, and convert it back once I have the logic correct. Or leave as that strangly Fortran IV-ish form:

        my( $p, $i ) = 0; loop { $p += $i; last if $i = 1+index $haystack, $needle, $p; }

        I know "LW is always right" and "LW is always right even when he is wrong", but on this I think he has never been more right.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: Useful uses of redo?
by glwtta (Hermit) on Aug 26, 2004 at 20:29 UTC
    I've used redo once in the last two years or so. Parsing a file with a rather involved format I had several subs to handle specific sections; these sections did not have a closing token of any kind, so the subs had to get a token to find out that they don't recognize it and that their section is over. Rather than futzing it so I can "unget" a token, it seemed easier to just return the current one and redo in the main loop.

    so:

    while(<FILE>){ if(/Foo/){ $_ = foo(); redo; } elsif { # do lots of other stuff } } sub foo { while(<FILE>){ if(/something or other/){ # so stuff } else { return $_; } } }

    Note that foo() doesn't see the line containing "Foo" nor did it need to in this case.

    Worked well, certainly don't know if that's doing the Right Thing.

Re: Useful uses of redo?
by davido (Cardinal) on Aug 27, 2004 at 00:03 UTC

    I'm posting a second follow-up to your thread, because I think that one important use of redo hasn't gotten proper attention (including in my previous followup).

    Some of the examples listed are using redo in a bare block, which essentially creates a loop whos termination is going to rely on logic within the block placing a call to last.

    But remember also that redo will repeat the current iteration of a loop, without going through the loop's conditional or next-iteration stage first. Consider the following example:

    my @array = qw/this that those these/; foreach my $word ( @array ) { print $word, "\t"; redo if $word =~ m/that/; }

    The output will be "this    that    that    that...." because redo repeats the current iteration without moving on to the next iterant (which would be 'those').

    This can be useful if you're trying to keep your loop working on the same iteration until some other condition is met. Perhaps you are blocking, or maybe you're waiting for a mouse click, etc.


    Dave

Re: Useful uses of redo?
by davido (Cardinal) on Aug 26, 2004 at 19:05 UTC

    Frankly, I feel that while(1){...} is the lie, compared to { ....; redo; }. And nobody will argue that there isn't usefulness in neverending loops (even if those loops end with a last in there somewhere).


    Dave

      I use for(;;){...} rather while(1){...} (less lying), but I like this redo idea. I didn't know it could be used on bare blocks before this thread. However, I'm concerned about the lack of visual junk at the top that people expect for loops. Do you think there's any penalty or side-effects to using do { ... } instead of { ... }?

        Do you think there's any penalty or side-effects to using do { ... } instead of { ... }?
        Other than it won't work? That's a pretty severe penalty. last/next/redo do not pay attention to do-blocks.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

        There isn't necessarily any reason not to use do { ... }, as long as you realize that a do block isn't 100% like bare blocks. I look at do blocks kind of like immediately executing subroutines minus the parameter list, since do blocks have return values vaguely similar to subs.

        If you're concerned about the visual ambiguity of a bare block with a redo inside it, you can always start it off with a label, as in:

        LOOP: { # do some stuff redo; }

        However, even though while(1) (and even for(;;)) sort of perpetrates a fib, it is still more flexible to use such constructs over the bare block / redo method, because bare blocks don't support continue, occasionally useful with explicit loops.


        Dave

Re: Useful uses of redo?
by strat (Canon) on Aug 27, 2004 at 07:33 UTC

    Well, I sometimes use redo to parse files with continuation lines, e.g. TCL-Code where \ at the end signs that the expression is continued in the next line...

    while (<TCL>) { if (s/\\\s*$//) { # if a \ is at the end of the line, remove it $_ .= <TCL>; # and append the next line redo; # and then redo with more of the expression } # if # do something with the expression } # while

    This code is not really waterproof for all Tcl-Scripts, since it is just a short example

    Best regards,
    perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"

Re: Useful uses of redo?
by sharkey (Scribe) on Aug 27, 2004 at 04:02 UTC
    I was writing a state machine the other day (due to the lack of spawn_child in mod_perl-1.99).

    In some cases the most natural progression (not the most academic) was to do X, change state, and process the current input again. This was a good place to use a redo.

Re: Useful uses of redo?
by Anonymous Monk on Aug 26, 2004 at 23:55 UTC
    I use redo a lot for loops with multiple steps, often involving database work that must be atomic, tied with failure-prone XMLRPC or http requests.... such as...
    while (my $loop = shift) { $db->rollback; $db->begin_transaction; my ($fail1, $fail2, $fail3); $fail1 = eval { # do something complicated to $loop; } $fail2 = eval { # do something more to $loop; } $fail3 = eval { # do something more to $loop; } unless ($fail1 && $fail2 && $fail3) { $db->rollback; redo; } $db->commit; }

    20040826 Edit by ysth: add code tags

Re: Useful uses of redo?
by Anonymous Monk on Aug 27, 2004 at 10:33 UTC
    # Print 10 random numbers between 0 and 100, without duplicates. my %seen; for (1 .. 10) { redo if $seen {my $r = int rand 100} ++; print "$r\n"; }
Re: Useful uses of redo?
by Aristotle (Chancellor) on Aug 29, 2004 at 20:32 UTC

    The canonical example is handling continuation lines.

    while ( <> ) { if ( s/\\$// ) { chomp; $_ .= <>; redo; } # .. }

    Here, the code tests if it can remove a backslash from the end of a line, and if so, gobbles another line from the input and tries again. If the new line ends in a backslash, it will redo again in the new iteration.

    NB: this would be more involved in real code — the above snippet is merely for demonstration and a little simpleminded and subtly buggy.

    Makeshifts last the longest.