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
| [reply] [d/l] |
|
|
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.
| [reply] |
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. | [reply] |
Insert new items into foreach list:
by ikegami (Patriarch) on Aug 26, 2004 at 18:20 UTC
|
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.
| [reply] [d/l] [select] |
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
- A bare block.
- A last if condition
- 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
| [reply] [d/l] [select] |
|
|
{ # 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
}
| [reply] [d/l] [select] |
|
|
do { } still exists. It's just the statement-modifier while-on-do that is taken away.
| [reply] [d/l] |
|
|
|
|
|
|
In Perl 6 that is done by
loop {
# do something
last if $done;
}
| [reply] [d/l] |
|
|
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
| [reply] [d/l] [select] |
|
|
|
|
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. | [reply] [d/l] |
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.
| [reply] [d/l] [select] |
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).
| [reply] [d/l] [select] |
|
|
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 { ... }?
| [reply] [d/l] [select] |
|
|
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.
| [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.
| [reply] [d/l] [select] |
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"
| [reply] [d/l] |
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.
| [reply] |
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 | [reply] [d/l] |
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";
}
| [reply] [d/l] |
Re: Useful uses of redo?
by Aristotle (Chancellor) on Aug 29, 2004 at 20:32 UTC
|
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.
| [reply] [d/l] |