Perl do... while и last command

Я только что столкнулся с очень странным поведением, которое я действительно не могу объяснить:

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

Приведенный выше код никогда не печатает "WHILE ENDED", хотя он, похоже, выходит из цикла while, когда $TEST >= 10.

Но следующий код печатает "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";

В обоих тестах начальное значение $TEST равно 0.

Является ли поведение last в do...while отличным от for и while {...}?

Ответы

Ответ 1

A do блок с модификатором цикла не считается реальным циклом до next, last и redo. Это упоминается в perlsyn, где вы найдете отзыв, который Шверн упомянул об окружающем его блоке, чтобы сделать работу last. Но это не будет работать с next, потому что пустой блок выполняется только один раз, поэтому next действует как last. Чтобы сделать работу next, вы можете поместить пустой блок внутри do, но тогда last будет действовать как next.

Если вам нужны как next, так и last для работы с do ... while, самый простой способ - использовать бесконечный цикл с реальным условием в блоке continue. Эти 2 цикла эквивалентны, за исключением того, что второй представляет собой реальный цикл, поэтому он работает с next и last:

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

Ответ 2

Из perldoc -f last:

"last" не может использоваться для выхода из блока, который возвращает значение, например "eval {}", "sub {}" или "do {}"

Ответ 3

TLP является правильным. Стандартная работа для этого (я просто попал в нее сам) - это обернуть do/while в незакрытом блоке, который, интуитивно-интуитивно, будет уважать элементы управления контуром.

{ do {
    last;
} while 1; }

Блок снаружи поймает last. Если вы хотите обрабатывать next, вам нужно поместить блок внутри.

do {{
    next;
}} while 1;

Внутри блока будет ломаться next.

К сожалению, вы не можете обойти оба.