http://www.perlmonks.org?node_id=748247

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

Why doesn't this work?

$ perl -le'do { print $i++; last if $i>5 } while 1; print "end"'
0
1
2
3
4
5
Can't "last" outside a loop block at -e line 1.

while (no pun intended) this works:

$ perl -le'while (1) { print $i++; last if $i>5 } print "end"'
0
1
2
3
4
5
end

Isn't do {} while as much a loop as while () {}?

Replies are listed 'Best First'.
Re: last in do vs last in while/for
by bellaire (Hermit) on Mar 04, 2009 at 18:24 UTC
    No, do {} while is a single statement, using while as postfix control. It's the same as print while() or sleep while()... Perl sees the do {} block as one statement (do) which happens to take a code block as an argument, and you can't use loop control statements within that block.

    You can use do with other postfix controls which are decidedly un-looplike, for example:
    do { print "Hello!"; } if (1);
    while() {} on the other hand is a genuine loop.

      Ok, but why works the following:

      $ perl -le'do { print $i++; last if $i>5 } for (1..10); print "end"' 0 1 2 3 4 5 end
        Seems that postfix for actually creates a loop. I wonder if that has always been the case.
        $ perl -MO=Concise,-exec -e'while (foo()) { bar() }' 3 <{> enterloop(next->8 last->d redo->4) v $ perl -MO=Concise,-exec -e'bar() while foo()' $ perl -MO=Concise,-exec -e'do { bar() } while foo()' $ perl -MO=Concise,-exec -e'for (foo()) { bar() }' 8 <{> enteriter(next->d last->g redo->9) lK $ perl -MO=Concise,-exec -e'bar() for foo()' 9 <{> enteriter(next->d last->g redo->a) lK

        (Output filtered using grep 'enterloop\|enteriter')

        bar() while foo() uses a goto instead. It is implemented almost identically to:

        LOOP_START: ( bar() ), goto LOOP_START if foo();

        do { bar() } while foo() is not readily representable in Perl, but it doesn't use a loop either. The closest I can get is

        LOOP_START: do { bar() }; goto LOOP_START unless foo();

        But yes, the answer is "Implementation Details". There's no real reason for it not to work except backwards compatibility.

Re: last in do vs last in while/for
by runrig (Abbot) on Mar 04, 2009 at 17:28 UTC
    When things don't behave like you think they should, reading the docs sometimes helps:
    "last" cannot be used to exit a block which returns a value such as "eval {}", "sub {}" or "do {}", and should not be used to exit a grep() or map() operation.
Re: last in do vs last in while/for
by baxy77bax (Deacon) on Mar 04, 2009 at 18:22 UTC
    'Isn't do {} while as much a loop as while () {}?'

    not exactly. if you are do-whileing then you are evaluating loop after you do something , so technically you are not in the loop, that is how you make sure that something is done (in the 'contents' of a loop) even if a condition is not good to go. and because perl complains ('Can't "last" outside a loop block at -e line 1.') it should be obvious that last is dependent on a loop

    hope this helps (please anyone correct if i'm wrong)

      It doesn't matter whether the it's bottom tested or not. For example, you can break out of bottom tested loops in Visual Basic.

      Also, consider

      local $\="\n"; print('a'); { print('b'); last; print('c'); } print('d');
      a b d

      versus

      local $\="\n"; print('a'); do { print('b'); last; print('c'); }; print('d');
      a b Can't "last" outside a loop block at a.pl line 5.

      See bellaire's post for the real reason.

Re: last in do vs last in while/for
by sundialsvc4 (Abbot) on Mar 04, 2009 at 22:31 UTC

    Suffice it to say that, two scenarios which are functionally equivalent might not be regarded as structurally equivalent by the compiler.

    Hey, as a last resort, we can always “appeal to a higher authority.” ;-) After all, “Thus Sprach Zarathustra The Implementors!!(ommmm.....)

    “If you don't like it, go write your own compiler!”   :-D