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

Posted

技术标签:

【中文标题】将两个或多个数组传递给 Perl 子例程【英文标题】:Passing two or more arrays to a Perl subroutine 【发布时间】:2011-08-06 12:19:18 【问题描述】:

我在子例程中传递和读取参数时遇到问题,该子例程应该有两个数组。

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

我从网上看到并尝试了几个例子,但没有一个对我有用。

【问题讨论】:

【参考方案1】:

有两种方法可以做到这一点:

    按原型 参考

但在我讨论这些之前——如果你在问题中展示的是关于你想做的事情的程度——让我建议List::MoreUtils::pairwise

那么,你会在哪里写这个:

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

你只需写这个:

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

按原型

这类似于push。 (就像 push 它要求在 something 上有一个 @ 印记)

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

当你这样做的时候就这样

two_array_sub( @a, @b );

它有效。而通常它只会在您的子列表中显示为一个长列表。正如您将在下面的讨论中看到的那样,它们并不适合所有人。

参考

这是每个人都向您展示的方式。

some_sub( \@a, \@b );

关于原型

他们很挑剔。如果您有 refs,这将不起作用:

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_levelan_array );
new_two_array_sub( $arr, @b );
new_two_array_sub( @a, @b );
new_two_array_sub( $arr, $self->a_levelan_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 ] );

然后子中的复用代码负责处理两个数组引用。

【讨论】:

【参考方案2】:

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

two_array_sum( \@a, \@b )

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

【讨论】:

【参考方案3】:

我将引用 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";

【讨论】:

@leden,perldoc 文章 perllol(Lists of Lists:perldoc.perl.org/perllol.html)和 perldsc(Data Structures Cookbook:perldoc.perl.org/perldsc.html)也可能有用。他们有很多具体的例子。 我同意您上面关于 Axeman 的回答客观/具体地更好的评论,但经过这么多年,我仍然没有足够地使用手册页。我想,用 PERL 学习它的方法不止一种。【参考方案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++];【参考方案5】:

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

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

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)。【参考方案6】:

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

sub two_array_sum 
  my ($x, $y) = @_;
  #process $x, $y;

two_array_sum(\@a, \@b);

【讨论】:

这只是把第一个数组的第一个值放在$x中,把第一个数组的第二个值放在$y中。【参考方案7】:

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

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

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

【讨论】:

以上是关于将两个或多个数组传递给 Perl 子例程的主要内容,如果未能解决你的问题,请参考以下文章

如何让 VBA 子例程调用将数组传递给子例程中的另一个函数的函数

将指向 uint16_t 的指针传递给需要 C 中 uint8_t[] 数组的子例程 - 如何?

如何将可分配数组传递给 Fortran 中的子例程

如何将 C_FLOAT 数组传递给 Fortran 子例程

在 FORTRAN 子例程中传递不同的变量集

将参数传递给 Perl 子例程时,是不是会影响数据复制性能?