Почему Perl's for () не проходит через все элементы в моем массиве? - PullRequest
3 голосов
/ 06 января 2010

Иметь головоломку Perl:

my @l = ('a', 'b', 'c');
for (@l) {
    my $n = 1;
    print shift @l while (@l and $n --> 0);
    print "\n";
}

Что это за печать? Должно быть a, b и c, верно? Но, подождите, на самом деле где-то ошибка, она печатает только a и b. Наверное, просто какая-то глупость, которую легко решить, верно?

Ладно, сделайте небольшое изменение кода, чтобы проверить все и измените @l на

my @l = ('a', 'b', 'c', 'd');

Что это за печать? Вероятно, a, b и c из-за того, что глупо один, верно? ... Подождите секунду, на самом деле он все еще печатает только a и b. Итак, ошибка в том, что он печатает только первые два символа.

Измените @l снова на

my @l = ('a', 'b', 'c', 'd', 'e');

Хм, теперь он печатает a, b и c. Но не д или е. Фактически, каждые 2 буквы, которые мы добавляем, заставляют печатать следующую букву в последовательности. Поэтому, если мы добавим f, он все равно будет просто печатать a, b и c, но если мы добавим f и g, он напечатает a, b, c и d.

Это также происходит с похожими результатами для разных значений $ n.

Так что здесь происходит?

Ответы [ 3 ]

15 голосов
/ 06 января 2010

Дэйв Уэбб опередил меня, но вот цитата из perldoc perlsyn, говорящая не делать этого:

Если какая-либо часть LIST является массивом, foreach очень запутается, если вы добавите или удалите элементы в теле цикла, например, с помощью splice. Так что не делай этого.

Обратите внимание, что ранее в тексте синтаксис foreach был описан как foreach LIST, то есть LIST, на который они ссылаются в документации. Также обратите внимание, что foreach и for эквивалентны.

14 голосов
/ 06 января 2010

Что происходит, вы используете for и shift одновременно. Таким образом, вы просматриваете список, изменяя его, не очень хорошая идея.

1 голос
/ 07 января 2010

Я думаю, что это чей-то гаджет-код. Это не похоже на то, как вы хотели бы написать что-нибудь. Но лучше всего это проиллюстрировать, что (по крайней мере, в некоторых версиях) Perl действительно выполняет более простой цикл for, где:

for ( @l ) { 
    #...
}

заменяется на:

for ( my $i = 0; $i < @l; $i++ ) { 
    local $_ = $l[$i];
    #...
}    

Таким образом, поскольку @l равно ( 'c' ), когда мы прошли дважды, наши поездки уже больше, чем scalar( @l ), так что мы ушли Я проверял это в ряде случаев, и они кажутся эквивалентными.

Ниже приведен код, который я написал для тестовых случаев. Из этого видно, что из-за сдвига, как только мы на полпути, цикл завершится.

use strict;
use warnings;
use English qw<$LIST_SEPARATOR>;
use Test::More 'no_plan';

sub test_loops_without_shifts { 
    my @l = @_;
    my @tests;
    for ( @l ) { 
        push @tests, $_;
    }
    my @l2 = @_;
    my $n  = @tests;
    my $i = 0;
    for ( $i = 0; $i < @l2; $i++ ) { 
        local $_ = $l2[$i];
        my $x = shift @tests;
        my $g = $_;
        is( $g, $x, "expected: $x, got: $g" );
    }
    is( $n, $i );
    is_deeply( \@l, \@l2, do { local $LIST_SEPARATOR = .', '; "leftover: ( @l ) = ( @l2 )" } );
    return $i;
}

sub test_loops { 
    my @l = @_;
    my @tests;
    for ( @l ) { 
        push @tests, shift @l;
    }
    my @l2 = @_;
    my $n  = @tests;
    my $i = 0;
    for ( $i = 0; $i < @l2; $i++ ) { 
        local $_ = $l2[$i];
        my $x = shift @tests;
        my $g = shift @l2;
        is( $g, $x, "expected: $x, got: $g" );
    }
    is( $n, $i );
    is_deeply( \@l, \@l2, do { local $LIST_SEPARATOR = ', 'c; "leftover: ( @l ) = ( @l2 )" } );
    return $i;
}

is( test_loops( 'a'..'c' ), 2 );
is( test_loops( 'a'..'d' ), 2 );
is( test_loops( 'a'..'e' ), 3 );
is( test_loops( 'a'..'f' ), 3 );
is( test_loops( 'a'..'g' ), 4 );
is( test_loops_without_shifts( 'a'..'c' ), 3 );
is( test_loops_without_shifts( 'a'..'d' ), 4 );
is( test_loops_without_shifts( 'a'..'e' ), 5 );
is( test_loops_without_shifts( 'a'..'f' ), 6 );
is( test_loops_without_shifts( 'a'..'g' ), 7 );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...