【问题标题】:Passing two or more arrays to a Perl subroutine将两个或多个数组传递给 Perl 子例程
【发布时间】: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

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

【问题讨论】:

    标签: arrays perl parameter-passing subroutine


    【解决方案1】:

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

    1. 按原型
    2. 参考

    但在我讨论这些之前——如果你在问题中展示的是关于你想做的事情的程度——让我建议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_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 ] );
    

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

    【讨论】:

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

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

        【讨论】:

          猜你喜欢
          • 2019-10-05
          • 1970-01-01
          • 2011-02-22
          • 1970-01-01
          • 2016-09-07
          • 2021-03-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多