Как передать два массива и строку в подпрограмму Perl?
Как передать два массива и строку в sub?
Вот что я пытаюсь сделать:
use strict;
use warnings;
my @x = qw(AAAA BBBB CCCC DDDD EEEE);
my @y = qw(1111 2222 3333 4444 5555);
my $z = "hello";
Hello(@x,@y,$z);
exit(0);
sub Hello {
my (@x,@y,$z) = @_;
print "$_\n" for @x;
print "$_\n";
print "$_\n" for @y;
print "$_\n";
print "$z\n";
}
Вывод:
AAA
BBBB
CCCC
DDDD
EEEE
1111
2222
3333
4444
5555
hello
Use of uninitialized value $_ in concatenation (.) or string at test.pl line 19.
Use of uninitialized value $_ in concatenation (.) or string at test.pl line 21.
Use of uninitialized value $z in concatenation (.) or string at test.pl line 22.
Ответы
Ответ 1
Вам нужно передать каждый из массивов в качестве ссылки, иначе ваш @x
в подпрограмме сожмет массив ENTIRE аргументов, оставив @y
пустой arraay и $z
значение undef.
Это происходит потому, что оператор запятой - в контексте списка - превратит a @x, @y, $z
в один массив, состоящий из всех элементов @x
, за которым следуют все элементы @y
, а затем значение $z
; ваш @x
в sub будет поглощать совокупный массив аргументов ENTIRE, оставляя @y
пустой массив и $z
значение undef.
Другим возможным источником путаницы является тот факт, что вы назвали обе переменные @x
, несмотря на то, что они полностью независимы друг от друга из-за правил определения области видимости. Хорошей практикой было бы назвать их чем-то отличным, чтобы не догадываться, какой из них вы хотели использовать, например. вызовите первый массив подпрограммы @x2
.
Обратите внимание, что вы можете передать массив в качестве ссылки одним из двух способов: ссылкой на исходный массив (реальный сквозной подход), а также ссылкой на COPY массива, который будет вести себя как вы хотели, чтобы ваш исходный код вел себя и проходил по значению.
use strict; use warnings;
my @x = qw(AAAA BBBB CCCC DDDD EEEE);
my @y = qw(1111 2222 3333 4444 5555);
my $z = "hello";
Hello(\@x,\@y,$z);
# If you wish to pass a reference of a COPY of the array,
# so that you can modify it inside the subroutine without modifying the original,
# instead call Hello([@x], [@y], $z);
exit(0);
sub Hello {
my ($x2,$y2,$z2) = @_;
# Now, you de-reference array reference $x2 via @$x2 or $x2->[$i]
# where previously you used @x2 or $x2[$i]
print "$_\n" for @$x2;
print "$_\n";
print "$_\n" for @$y2;
print "$_\n";
print "$z2\n";
}
Ответ 2
вам нужно использовать ссылки:
sub Hello {
my ($x, $y, $z) = @_;
print "$_\n" for @$x;
...
}
Hello(\@x, \@y, $z);
вы также можете использовать прототипы подпрограммы Perl, чтобы исключить \
на сайте вызова:
sub Hello (\@\@$) {...}
Hello(@x, @y, $z);
Прототип (\@\@$)
сообщает компилятору, что аргументы Hello
будут иметь контекст ссылки на массив в первых двух аргументах и скалярный контекст на третьем arg. Прототипы не предназначены для проверки аргументов, они позволяют вам писать подпрограммы, которые работают как встроенные. В этом случае, как push
.
Как упоминает DVK в комментарии ниже, PBP рекомендует не использовать прототипы в качестве общего правила, потому что они, как правило, используются неправильно для проверки аргументов. Я считаю, что использование определенных свойств прототипов (\%
или \@
для наложения ссылочного контекста для множественных типов или &
в качестве первого аргумента, чтобы разрешить синтаксис блока типа map
), оправдывает разрыв с PBP. Особенно верно, если sub используется для расширения синтаксиса Perl (вспомогательные функции, функции, которые являются просто синтаксическим сахаром...)
Ответ 3
Вам нужно передать ссылки на массивы:
sub Hello {
my ( $x_ref, $y_ref, $z ) = @_;
my @x = @$x_ref;
my @y = @$y_ref;
...
}
Hello( \@x, \@y, $z );
Ответ 4
use strict; use warnings;
my @x = qw(AAAA BBBB CCCC DDDD EEEE); my @y = qw(1111 2222 3333 4444 5555);
my $z = "hello";
Hello(\@x,\@y,$z);
exit(0);
sub Hello {
my ($x,$y,$z) = @_;
print "$_\n" for @$x;
print "$_\n" for @$y;
print "$z\n";
}