snoofkin snoofkin - 6 months ago 25
Perl Question

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 {...}
?

cjm cjm
Answer

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 };