Подтвердить что ты не робот

Список:: Функция MoreUtils mesh или 'zip'

Итак, этот вопрос предназначен исключительно для учебных целей и любопытства, но может ли кто-нибудь объяснить, как работает эта функция ниже?

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) {
    my $max = -1;
    $max < $#$_ && ( $max = $#$_ ) foreach @_;
    map {
        my $ix = $_;
        map $_->[$ix], @_;
    } 0 .. $max;
}

Он из модуля List::MoreUtils. Я использую его в одном из своих приложений, и мне довелось увидеть исходный код, и это заставило меня почувствовать, что я совсем не знаю перла! Может ли кто-нибудь объяснить это безумие?:) Спасибо!

4b9b3361

Ответ 1

Я не буду освещать часть прототипов (толпа говорит, что он будет).

Здесь более читаемая версия - в идеале она должна быть понятной

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) {

    # Calculate the maximum number of elements in each of the array refs 
    # we were passed:

    my $maxLength = 0;
    foreach my $array_ref (@_) { # @_ is all arrey refs passed in
        if ($maxLength < @$array_ref) { 
            # we found an array longer than all previous ones 
            $maxLength = @$array_ref;
        }
    }

    # If you represent the arrays as a matrix:
    #   ARR1 = [ a1e1, a1e2, .... a1eN],
    #   ARR2 = [ a2e1, a2e2, .... a2eN],
    #    ...
    #   ARR2 = [ aMe1, aMe2, .... aMeN];
    # Then, we are simply walking through the matrix;
    # each column top to bottom then move on to next column left to right
    # (a1e1, a2e1, ... aMe1, a1e2, a2e2, ... aMeN)

    my @results;
    for (my $index = 0; $index < $maxLength; $index++) { # Iterate over columns
         foreach my $array_ref (@_) { # Iterate over per-row cells in each column
             push @results, $array_ref->[$index];
         }
    } ;
}

здесь прокомментированная оригинальная версия

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) {

    # Calculate the largest index in each of the array refs
    # @_ is an array of all the input arrayrefs
    # $_ will be one of the array refs in a foreach loop
    # $#{$X} is the largest index in arrayref X; thus
    # $#$_ is the largest index in arrayref $_
    my $max = -1;
    $max < $#$_ && ( $max = $#$_ ) foreach @_;

    # Return a list, obtained by looping 
    # over every index from 0 to the maximal index of any of the arrays
    # Then, for each value of the index ($ix), push into the resulting list
    # an element with that index from each of the arrays.
    map {
        my $ix = $_;
        map $_->[$ix], @_;
    } 0 .. $max;
}


Одна из необычных вещей в этом методе - function signature (prototype).

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) {

Как @mob и @ikegami разумно отметили в комментариях,

... Он инструктирует Perl ожидать от 2 до 32 названных массивов и передать их функции (в @_) в качестве ссылок на массивы. Поэтому, если вы вызываете mesh @a,@b,@c, тогда @_ в сетке устанавливается (\@a,\@b,\@c) вместо одного "плоского" списка со всеми отдельными элементами @a, @b, and @c (mob)
 ... Их технически не нужно называть, просто разыменовывать. например @$ref и @{[qw( foo bar )]} работают так же, как @a. Другими словами, он должен начинаться с @ (а не быть срезом). (Ikegami)

Другими словами, следующие 2 вызова ведут себя одинаково

my @a1 = (1);
my @a2 = (2);
sub mesh_prototype(\@\@) { print "$_->[0]\n" }
sub mesh_pass_arrayref() { print "$_->[0]\n" }
mesh_prototype(@a1, @a2);
mesh_pass_arrayref(\@a1, \@a2);

Это делается для того, чтобы вы могли передавать отдельные массивы (а не arrayrefs) в качестве аргументов для функций, которые будут вести себя как встроенные (например, map/sort)

Чтобы ответить на запрос Zaid о том, что произойдет, если в качестве параметров для вызова mesh() будут указаны 1 или 33 массива, он будет генерировать ошибку времени компиляции:

Not enough arguments for main::mesh at mesh.pl line 16, near "@a1)"
Execution of mesh.pl aborted due to compilation errors.

Too many arguments for main::mesh at mesh.pl line 16, near "@a2)"
Execution of mesh.pl aborted due to compilation errors.