将两个或更多数组传递给Perl子例程

14

我在子程序中遇到了问题,无法传递和读取两个数组作为参数。

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个回答

30

你可以用两种方法来实现这个:

  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 4& 来关闭 Perl 对此的警告。

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

然后,子程序中的mux-ing代码负责处理两个数组引用。


7

将您的数组引用传递给函数:

two_array_sum( \@a, \@b )

不要使用ab作为变量名,因为$a$b是特殊的(用于排序)。


6

我会引用来自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";

2
@leden,perldoc文章perllol(列表的列表:http://perldoc.perl.org/perllol.html)和perldsc(数据结构手册:http://perldoc.perl.org/perldsc.html)也可能会很有用。它们有许多具体的示例。 - daotoad
我同意你上面的评论,Axeman的答案客观/明确更好,但这么多年过去了,我仍然不够用man页面。我猜学习PERL有不止一种方法。 - aschultz

4
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;
}

那将是:my $bItem = $bRef->[$idx++]; - shawnhcorey

3

您不能将数组传递给函数。函数只能接受标量值的列表作为参数。因此,您需要传递足够数据以重新创建数组的标量值。

最简单的方法是传递数组的引用。

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

@kindahero,是的。第二个函数使用的内存不会超过返回结果所需的内存。如果返回值是数组引用,则可以使用更少的内存(通过从map切换到for)。 - ikegami

2

您需要使用引用将数组或哈希传递给子程序,例如:

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

这只将第一个数组的第一个值放入$x中,将第一个数组的第二个值放入$y中。 - Zac

0

这些方法是规范的。另一种方法是:

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 就可以转储整个结构 - 我可以更好地控制数据的范围,并且可以轻松地传递整个结构。

我经常发现自己将多个这样的结构传递给子例程。作为标量,它们非常好地传递,谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接