Передача двух или более массивов в подпрограмму Perl

У меня возникли проблемы с передачей и чтением аргументов внутри подпрограммы, которая, как ожидается, будет иметь два массива.

sub two_array_sum { # two_array_sum ( (1 2 3 4), (2, 4, 0, 1) ) -> (3, 6, 3, 5)
  # I would like to use parameters @a and @b as simply as possible
}

# I would like to call two_array_sum here and pass two arrays, @c and @d

Я видел и пробовал несколько примеров из интернета, но ни один из них не работал для меня.

7 ответов


есть два способа сделать это:

  1. прототип
  2. по ссылке
List::MoreUtils::pairwise

Итак, где бы вы написали это:

my @sum = two_array_sum( @a, @b )

вы просто напишете это:

my @sum = pairwise { $a + $b } @a, @b;

прототип

это работает, как push. (И прямо как push он требует, чтобы @ сигил on что-то)

sub two_array_sub (\@\@) { 
    my ( $aref, $bref ) = @_;
    ...
}

таким образом, когда вы делаете это

two_array_sub( @a, @b );

это работает. В то время как обычно он просто появляется в вашем sub как один длинный список. Они не для всех, как вы увидите в моем обсуждении ниже.

по ссылке

это то, как все показывают вам.

some_sub( \@a, \@b );

о прототипах

они привередливы. Это не будет работать, если у вас есть ссылки:

two_array_sub( $arr_ref, $brr_ref );

вы должны пройти их так:

two_array_sub( @$arr_ref, @$brr_ref );

однако, потому что "выбора выражений" получает действительно уродливый быстро с массивами, вложенными глубоко, я часто избегаю суетливости Perl, поскольку вы можете перегрузить тип ссылки Perl, поместив его в конструкцию" символьного класса". \[$@] означает, что ссылка может быть скаляром или массивом.

sub new_two_array_sub (\[$@]\[$@]) { 
    my $ref = shift;
    my $arr = ref( $ref ) eq 'ARRAY' ? $ref : $$ref; # ref -> 'REF';
    $ref    = shift;
    my $brr = ref( $ref ) eq 'ARRAY' ? $ref : $$ref;
    ...
}

Итак, все это работает:

new_two_array_sub( @a, $self->{a_level}{an_array} );
new_two_array_sub( $arr, @b );
new_two_array_sub( @a, @b );
new_two_array_sub( $arr, $self->{a_level}{an_array} );

однако, Perl все еще суетится по этому поводу... почему-то:

new_two_array_sub( \@a, $b );
OR 
new_two_array_sub( $a, [ 1..3 ] );

или любой другой "конструктор", который все еще можно рассматривать как ссылку на массив. К счастью, Вы можете заткнуть Perl об этом со старым Perl 4 &

&new_two_array_sub( \@a, [ 1..3 ] );

затем код mux-ing в sub заботится об обработке двух ссылок на массив.


передайте ссылки на ваши массивы функции:

two_array_sum( \@a, \@b )

и не использовать a или b как имена переменных, потому что $a и $b специальные (для сортировки).


Я процитирую из man perlref но вы должны прочитать все это:

   Making References

   References can be created in several ways.

   1.  By using the backslash operator on a variable, subroutine, or
       value.  (This works much like the & (address-of) operator in C.)
       This typically creates another reference to a variable, because
       there's already a reference to the variable in the symbol table.
       But the symbol table reference might go away, and you'll still have
       the reference that the backslash returned.  Here are some examples:

           $scalarref = $foo;
           $arrayref  = \@ARGV;
           $hashref   = \%ENV;
           $coderef   = \&handler;
           $globref   = \*foo;

...

   Using References

   That's it for creating references.  By now you're probably dying to
   know how to use references to get back to your long-lost data.  There
   are several basic methods.

   1.  Anywhere you'd put an identifier (or chain of identifiers) as part
       of a variable or subroutine name, you can replace the identifier
       with a simple scalar variable containing a reference of the correct
       type:

           $bar = $$scalarref;
           push(@$arrayref, $filename);
           $$arrayref[0] = "January";
           $$hashref{"KEY"} = "VALUE";
           &$coderef(1,2,3);
           print $globref "output\n";

my @sums = two_array_sum(\@aArray, \@bArray);

sub two_array_sum { # two_array_sum ( (1 2 3 4), (2, 4, 0, 1) ) -> (3, 6, 3, 5)
    my ($aRef, $bRef) = @_;
    my @result = ();

    my $idx = 0;
    foreach my $aItem (@{$aRef}) {
        my $bItem = $bRef->[$idx++];
        push (@result, $aItem + $bItem);
    }

    return @result;
}

вам нужно передать массивы или хэши вашей подпрограмме, используя ссылки, например:

sub two_array_sum {
  my ($x, $y) = @_;
  #process $x, $y;
}
two_array_sum(\@a, \@b);

вы не можете передавать массивы в функции. Функции могут принимать только списки скаляров для аргумента. Таким образом, вам нужно передать скаляры, которые предоставляют достаточные данные для воссоздания массивов.

самым простым способом сделать это является передача ссылок на массивы.

sub two_array_sum {
   my ($array0, $array1) = @_;

   my @array0 = @$array0;
   my @array1 = @$array1;

   return map { $array0[$_] + $array1[$_] } 0..$#array0;
}

вы даже можете избежать восстановления массивов и работы со ссылками напрямую.

sub two_array_sum {
   my ($array0, $array1) = @_;
   return map { $array0->[$_] + $array1->[$_] } 0..$#$array0;
}

использование:

my @array0 = (1, 2, 3, 4);
my @array1 = (2, 4, 0, 1);
two_array_sum(\@array0, \@array1);

квадратные скобки построить анонимный массив (заполняется результат выражения внутри) и возвращает ссылку на этот массив. Таким образом, можно записать следующим образом:

two_array_sum([1, 2, 3, 4], [2, 4, 0, 1]);

эти методы являются каноническими. Другой способ сделать это:

use strict;
my $data;

@{$data->{array1}} = qw(foo bar baz);
@{$data->{array2}} = qw(works for me);
testsub($data);

sub testsub
{
    my ($data) = @_;
    print join "\t", @{$data->{array1}}, "\n";
    print join "\t", @{$data->{array2}}, "\n";
    $data->{array1}[3] = "newitem";
    delete $data->{array2};
    push @{$data->{newarray}}, (1, 2, 3);
    return $data;
}

когда вы делаете это таким образом, вы можете держать гораздо более жесткий контроль над своими переменными, а не страдать от крысиного гнезда программных данных, смешанных с конфигурационной информацией.

В общем, у меня никогда не бывает более трех или четырех переменных в любой программе.

Я также поддерживаю систему - я использую хэши списков хэшей списков.

$config->{server}[0]{prod}[0]{input}[0] = 'inputfile';

причина в том, что как до тех пор, пока я согласуюсь с чередованием каждого пути,Data::Dumper может сбросить всю структуру - и я могу лучше контролировать объем данных и могу легко передавать целые структуры.

Я часто передаю несколько таких структур подпрограммам. Как скаляры, они проходят довольно хорошо, спасибо.