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.

DAG
  • 157
  • 1
  • 2
  • 6

6 Answers6

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 (...);
ysth
  • 96,171
  • 6
  • 121
  • 214
  • 3
    This is mentioned in [perlsyn](http://perldoc.perl.org/perlsyn.html#Statement-Modifiers), although I think it's clearer if you use a label on your bare block and in your `last` statement. – cjm Dec 06 '10 at 20:30
  • 4
    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. – Mecki Feb 04 '14 at 20:05
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.

caveman
  • 1,755
  • 1
  • 14
  • 19
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.

  • +1 for the Delphi mention :) . I really enjoyed Delphi. I also used its linker with a DOS C compiler to write Win32 C programs :) . – cxw Apr 17 '18 at 13:45
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.

David W.
  • 105,218
  • 39
  • 216
  • 337
  • 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. – DVK Dec 06 '10 at 19:55
  • If only this and any number of other things could have stopped VBscript from catching on. – cikkle Dec 06 '10 at 22:00
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'...

lm2s
  • 388
  • 1
  • 7
  • 22
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 ,)

cxw
  • 16,685
  • 2
  • 45
  • 81