snoofkin
snoofkin

Reputation: 8895

Perl do...while and last command

I've just encountered some very weird behavior that I really can't explain:

do {
    my $qry = $self->getHTMLQuery(undef, $mech->content());
    next if (!defined($qry));

    push(
        @prods,
        map { 'http://www.XXXXYYYX.com'.$_->attr('href') }
            $qry->query('div.prodInfo div.prodInfoBox a.prodLink.GridItemLink')
    );

    $qry->delete();
    $TEST++;

    last if ($TEST >= 10);

} while(eval { $mech->follow_link(class => 'jump next') });

print "WHILE ENDED\n";

The code above never prints "WHILE ENDED" even though it does seem to go out of the while loop when $TEST >= 10.

But the following code does print "WHILE ENDED":

do {
    my $qry = $self->getHTMLQuery(undef, $mech->content());
    next if (!defined($qry));

    push(
        @prods,
        map { 'http://www.XXXXYYYX.com'.$_->attr('href') }
            $qry->query('div.prodInfo div.prodInfoBox a.prodLink.GridItemLink')
    );

    $qry->delete();
    $TEST++;

} while(eval { $mech->follow_link(class => 'jump next') } && $TEST <= 10);

print "WHILE ENDED\n";

In both tests, the initial value of $TEST is 0.

Is the behavior of last in do...while different than in for and while {...}?

Upvotes: 20

Views: 12407

Answers (3)

Schwern
Schwern

Reputation: 164769

TLP is right. The standard work around for this (I just hit it myself) is to wrap the do/while in a bare block which, counter-intuitively, does respect loop controls.

{ do {
    last;
} while 1; }

The block outside will catch last. If you want to handle next you have to put the bloc inside.

do {{
    next;
}} while 1;

The block inside will catch next.

Unfortunately you can't do both.

Upvotes: 18

cjm
cjm

Reputation: 62099

A do block with a looping modifier doesn't count as a real loop as far as next, last, and redo are concerned. This is mentioned in perlsyn, where you'll find the tip Schwern mentioned about surrounding it with a bare block to make last work. But that won't work with next, because a bare block is only executed once, so next acts like last. To make next work, you can put the bare block inside the do, but then last will act like next.

If you need both next and last to work with a do ... while, the easiest way is to use an infinite loop with the real condition in a continue block. These 2 loops are equivalent, except that the second is a real loop, so it works with next & last:

do { ... } while condition;
while (1) { ... } continue { last unless condition };

Upvotes: 31

TLP
TLP

Reputation: 67900

From perldoc -f last:

"last" cannot be used to exit a block that returns a value such as "eval {}", "sub {}" or "do {}"

Upvotes: 22

Related Questions