Alternative to "last" in do loops
Asked Answered
U

6

9

According to the perl manual for for last (http://perldoc.perl.org/functions/last.html), last can't be used to break out of do {} loops, but it doesn't mention an alternative. The script I'm maintaining has this structure:

do {
    ...
    if (...) 
    {
        ...
        last;
    }
} while (...);

and I'm pretty sure he wants to go to the end of the loop, but its actually exiting the current subroutine, so I need to either change the last or refactor the whole loop if there is a better way that someone can recommend.

Unheard answered 6/12, 2010 at 18:13 Comment(1)
The alternative is to not use a do {}.Homy
M
10

do BLOCK while (EXPR) is funny in that do is not really a loop structure. So, last, next, and redo are not supposed to be used there. Get rid of the last and adjust the EXPR to evaluate false when that situation is found. Also, turn on strict, which should give you at least a warning here.

Michaud answered 6/12, 2010 at 18:22 Comment(0)
C
17

Wrap the do "loop" in a bare block (which is a loop):

{
    do {
        ...
        if (...) 
        {
            ...
            last;
        }
    } while (...);
}

This works for last and redo, but not next; for that place the bare block inside the do block:

do {{
    ...
    if (...) 
    {
        ...
        next;
    }
    ...
}} while (...);
Credential answered 6/12, 2010 at 18:34 Comment(2)
This is mentioned in perlsyn, although I think it's clearer if you use a label on your bare block and in your last statement.Adnopoz
I always knew it, Perl is a sick language! do-while-loops are actually no loops but bare blocks are loops ::facepalm:: Anyway, I like Perl nonetheless as it is so wonderfully hacky and you can solve complex problems quick and dirty, "very dirty" ;-) +1 for your solution.Redfin
M
10

do BLOCK while (EXPR) is funny in that do is not really a loop structure. So, last, next, and redo are not supposed to be used there. Get rid of the last and adjust the EXPR to evaluate false when that situation is found. Also, turn on strict, which should give you at least a warning here.

Michaud answered 6/12, 2010 at 18:22 Comment(0)
H
2

Never a fan of do/while loops in Perl. the do isn't really a loop which is why last won't break out of it. In our old Pascal daze you couldn't exit a loop in the middle because that would be wrong according to the sage Niklaus "One entrance/one exit" Wirth. Therefore, we had to create an exit flag. In Perl it'd look something like this:

my $endFlag = 0;
do {
    ...
    if (...) 
    {
        ...
        $endFlag = 1;
    }
} while ((...) and (not $endFlag));

Now, you can see while Pascal never caught on.

Handsaw answered 6/12, 2010 at 19:3 Comment(2)
Pascal was not that bad as far as teaching languages go :) But I'm soooo glad I will never ever have to look at (or heavens forbit write) another line of Pascal code ever again.Ellissa
If only this and any number of other things could have stopped VBscript from catching on.Counterproposal
L
2

Why not just use a while loop?

while (...) {
  ...
  if (...) {
    last;
  }
}

You might have to change your logic slightly to accommodate the fact that your test is at the beginning instead of end of your loop, but that should be trivial.

By the way, you actually CAN break out of a Pascal loop if you're using Delphi, and Delphi DID catch on for a little while until Microsoft wised up and came out with the .net languages.

Lignin answered 4/1, 2013 at 19:35 Comment(1)
+1 for the Delphi mention :) . I really enjoyed Delphi. I also used its linker with a DOS C compiler to write Win32 C programs :) .Jenness
E
1

@ "http://perldoc.perl.org/functions/last.html": last cannot be used to exit a block that returns a value such as eval {} , sub {} or do {} , and should not be used to exit a grep() or map() operation.

So, use a boolean in the 'while()' and set it where you have 'last'...

Etherify answered 6/12, 2010 at 18:17 Comment(0)
J
0

Late to the party - I've been messing with for(;;) recently. In my rudimentary testing, for conditional expressions A and B, what you want to do with:

do {
    last if A;
} while(B);

can be accomplished as:

for(;; B || last) {
    last if A;
}

A bit ugly, but perhaps not more so than the other workarounds :) . An example:

my $i=1; 
for(;; $i<=3 || last) { 
    print "$i ";
    ++$i;
}

Outputs 1 2 3. And you can combine the increment if you want:

my $i=1;
for(;; ++$i, $i<=3 || last) { 
    print "$i ";
}

(using || because it has higher precedence than ,)

Jenness answered 17/4, 2018 at 13:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.