将数组和标量传递给子例程

时间:2016-03-29 18:36:14

标签: arrays perl reference

第一次在这里发帖,希望有人可以帮助我。我是编程和perl的新人,目前正在大学修读课程。

我的问题是关于以下子程序,它将数组和标量(字符串)作为参数。我通过引用数组调用子例程,并在子例程中取消引用。

作为参数传递给子程序的引用数组的长度不为0.在子程序中取消引用后得到的数组长度为0.我不明白为什么。

sub calc_average {

    my @indexes = @{$_[1]};
    print @indexes."\n"; #test only
    my $line = $_[0];
    #my $sum = 0;
    #my @row=split(/\t/,$line);
    #for (my $i=0; $i<@indexes; $i++){
    #    my $sum = $sum + $row[$indexes[$i]];
    #}
    #my $average = $sum/scalar(@indexes);
    #print $average."\n"; #test only
    #return $average;
}

这打印0.子程序用:

调用
calc_average($line, \@GC_indexes)."\n";

@GC_indexes不是空的。 $line是文件中的一行文字。

提前致谢!

3 个答案:

答案 0 :(得分:4)

my $sum = $sum + $row[$indexes[$i]];不应在循环中使用my声明。

您正在为$sum循环的每次迭代创建一个新的for(在for循环的范围内)。当循环结束时,在那里创建的$sum超出范围,程序使用您在循环之前创建的$sum(等于0)。

答案 1 :(得分:1)

您可以将数组传递给子函数的另一种方法如下

calc_average($line,\@GC_indexes);

sub calc_average{
    my ($line,$index) = @_;
    my @indexes = @{$index};
    print scalar(@indexes); #should print the length of your array

}

你可以填写剩下的部分,我以为我会分享。

答案 2 :(得分:0)

而不是你得到的解决方案,它对我来说很好评论。您的问题应该包含一个可验证的示例。请阅读How to create a Minimal, Complete, and Verifiable example

评论你所知道的代码不起作用将无法帮助你找到解决方案:它让我们无需工作。在这种情况下,我所要做的就是删除你的评论

这是您的问题的MCVE

use strict;
use warnings 'all';

use Data::Dump;

my $line = join "\t", 1 .. 100;
my @GC_indexes = (7, 14, 42);

print calc_average($line, \@GC_indexes), "\n";

sub calc_average {

    my @indexes = @{$_[1]};
    print @indexes."\n"; #test only
    my $line = $_[0];
    my $sum = 0;
    my @row=split(/\t/,$line);
    for (my $i=0; $i<@indexes; $i++){
        my $sum = $sum + $row[$indexes[$i]];
    }
    my $average = $sum/scalar(@indexes);
    print $average."\n"; #test only
    return $average;
}

输出

3
0
0

正如您所知,主要问题是您在$sum循环中声明了 new for。每次循环时,内部$sum被声明并以值undef开始,然后它增加$row[$indexes[$i]]并在循环块结束时被丢弃。您打算使用之前声明过几行的外部 $sum

请注意以下几点

  • 必须始终使用use strictuse warnings 'all'启动每个Perl程序。这将极大地帮助您可能会忽略大量的微不足道的错误,并且在您向其他人寻求帮助之前应该采取适当的措施

  • 使用更多的空格可以清楚地了解程序的基本单位的开始和停止位置。例如,操作符应该与它们的操作数分开,并且块应该与其周围的代码明显区分

  • 标量上下文中的数组计算数组中元素的数量;当插入到字符串中时,它们形成一个由内置变量$"的内容分隔的字符串,通常作为单个空格,在列表上下文中(如子程序调用的参数中),它们被评估为列表他们的元素

    这意味着print @indexes."\n"将打印@indexes中的元素数量,后跟换行符,而print "@indexes\n"将打印@indexes内容使用换行符,print @indexes, "\n"将打印数组的所有元素(没有分隔符)和换行符

    我不确定你的意思

  • 很少需要C风格的for循环。通常你只需要迭代一个列表。所以for (my $i=0; $i<@indexes; $i++)几乎肯定会更好for my $i ( 0 .. $#indexes )

    你几乎不需要$i++。该表单已经被C ++语言的名称所推广,但是递增$i也有在递增之前返回值的负担。大多数现代语言都会优化它,但你不应该依赖它,它也打破了将动词放在名词之前的规则

    表达式$#array的计算结果为@array的最后一个索引。它被设计用于像这样的实例

  • 表达式$sum/scalar(@indexes)不需要scalar运算符。如前所述,标量上下文中的@array(由/运算符提供)评估@array中元素的数量,因此您只需要$sum/@indexes

我写过这个解决方案。你自己的子程序(删除了注释)是calc_average0,而我写的是calc_average1。正如我所描述的那样,你自己的子程序打印引用传递的数组@GC_indexes中的元素数,然后是零,因为外部$sum永远不会从其初始化改变

我希望这会有所帮助

use strict;
use warnings 'all';

use Data::Dump;

my $line = join "\t", 1 .. 100;
my @GC_indexes = ( 7, 14, 42 );

calc_average0( $line, \@GC_indexes );
calc_average1( $line, \@GC_indexes );

sub calc_average0 {

    my @indexes = @{ $_[1] };
    print @indexes . "\n";    #test only
    my $line = $_[0];
    my $sum  = 0;
    my @row  = split( /\t/, $line );
    for ( my $i = 0; $i < @indexes; $i++ ) {
        my $sum = $sum + $row[ $indexes[$i] ];
    }
    my $average = $sum / scalar(@indexes);
    print $average. "\n";     #test only
    return $average;
}

sub calc_average1 {

    my ( $line, $indexes ) = @_;

    my @line = split /\t/, $line;

    my $sum = 0;
    $sum += $line[ $indexes->[$_] ] for 0 .. $#{$indexes};

    my $average = $sum / @$indexes;
    print "calc_average1: $average\n";    #test only

    return $average;
}

输出

3
0
calc_average1: 22