Передача двух или более массивов в подпрограмму Perl - PullRequest
12 голосов
/ 15 апреля 2011

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

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 ]

29 голосов
/ 15 апреля 2011

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

  1. по прототипу
  2. по ссылке

Но прежде чем обсуждать это - если то, что вы показываете в своем вопросе, касается степени того, что вы хотите сделать, - позвольте мне предложить List::MoreUtils::pairwise

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

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

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

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

По прототипу

Это работает как push. (И так же, как push, требуется @ сигил на что-то )

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

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

two_array_sub( @a, @b );

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

По ссылке

Именно так все вам показывают.

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 ] );

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

7 голосов
/ 15 апреля 2011

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

two_array_sum( \@a, \@b )

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

6 голосов
/ 15 апреля 2011

Я приведу от 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";
4 голосов
/ 15 апреля 2011
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;
}
2 голосов
/ 15 апреля 2011

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

Самый простой способ сделать это - передать ссылки на массивы.

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]);
2 голосов
/ 15 апреля 2011

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

sub two_array_sum {
  my ($x, $y) = @_;
  #process $x, $y;
}
two_array_sum(\@a, \@b);
0 голосов
/ 15 апреля 2011

Эти методы канонические.Еще один способ сделать это:

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 может создавать дамп всей структуры - и я могу лучше контролировать объем данных и передавать всеструктуры с легкостью.

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

...