【问题标题】:Running a nested while loop inside a foreach loop in Perl在 Perl 的 foreach 循环中运行嵌套的 while 循环
【发布时间】:2015-03-30 00:38:31
【问题描述】:

我正在尝试使用foreach 循环遍历数组,然后使用嵌套的while 循环遍历文本文件的每一行,以查看数组元素是否与一行文本匹配;如果是这样,那么我将该行中的数据推送到一个新数组中以执行计算。

外部 foreach 循环似乎工作正常(基于每个数组元素的打印结果),但内部 while 循环没有循环(每次将相同的数据推入数组)。

有什么建议吗?

代码如下

#! /usr/bin/perl -T

use CGI qw(:cgi-lib :standard);

print "Content-type: text/html\n\n";

my $input       = param('sequence');
my $meanexpfile = "final_expression_complete.txt";

open(FILE, $meanexpfile) or print "unable to open file";

my @meanmatches;

@regex = (split /\s/, $input);

foreach $regex (@regex) {

    while (my $line = <FILE>) {
        if ( $line =~ m/$regex\s(.+\n)/i ) {
            push(@meanmatches, $1);
        }
    }

    my $average                  = average(@meanmatches);
    my $std_dev                  = std_dev($average, @meanmatches);
    my $average_round            = sprintf("%0.4f", $average);
    my $stdev_round              = sprintf("%0.4f", $std_dev);
    my $coefficient_of_variation = $stdev_round / $average_round;
    my $cv_round                 = sprintf("%0.4f", $coefficient_of_variation);

    print font(
        { color => "blue" }, "<br><B>$regex average: $average_round   
&nbspStandard deviation: $stdev_round&nbspCoefficient of 
variation(Cv): $cv_round</B>"
    );
}

sub average {
    my (@values) = @_;
    my $count    = scalar @values;
    my $total    = 0;

    $total += $_ for @values;

    return $count ? $total / $count : 0;
}

sub std_dev {
    my ($average, @values) = @_;
    my $count       = scalar @values;
    my $std_dev_sum = 0;

    $std_dev_sum += ($_ - $average)**2 for @values;

    return $count ? sqrt($std_dev_sum / $count) : 0;
}

【问题讨论】:

  • 您计算averages is flawed 的方式,您的标准差将灾难性地失败。见Algorithms for calculating variance。当您使用它时,您可能需要无偏的标准差估计器。
  • 另外,这个 CGI 脚本在哪里运行?在我看来,任何可以调用这个脚本的人都可以用它做一些有趣的事情。
  • 我已经对您的 Perl 代码进行了布局,以便它更具可读性。您自己必须这样做,这样您就可以发现任何范围界定问题,并且只有在您希望帮助调试它时发布可读代码才是礼貌的。出于同样的原因,您必须始终use strictuse warnings 放在您编写的每个Perl 程序的顶部。它们是发现代码问题的宝贵帮助,如果没有use strict,使用my 声明任何内容都毫无意义。在这种情况下,use strict 表明您未能声明 @regex$regex
  • @SinanÜnür:你的博文很吸引人。我经常使用类似的东西来计算滚动统计,但没想到准确性会有所提高。 (我可能知道 Don Knuth 参与其中。)但我认为你夸大了“正确”做事的重要性。正如您自己所说,“现实世界的一个可取之处在于,给定变量不太可能包含具有如此极端范围的值”,我希望任何必须这样做的人都能明白这一点为这样一个不寻常的数据集编写需要特殊方法的数据。
  • @Borodin 最后你尝试了方差实验吗?那是一组彼此接近的数字。必须借助一些人为的例子一步一步地说明问题,但在现实生活中,你往往会做更多的总结。稳定计算很重要。

标签: perl loops


【解决方案1】:

是的,我的建议是:

  • 开启strictwarnings
  • 完善你的代码,
  • 使用 3 个参数打开:open ( my $inputfile, "&lt;", 'final_expression.txt' );
  • die 如果它没有打开 - 你的程序的其余部分是无关紧要的。
  • chomp $line
  • 您正在迭代您的文件句柄,但是一旦您完成此操作,您就处于文件的末尾以进行foreach 循环的下一次迭代,因此您的while 循环变为空操作。简单来说,将文件读入数组 my @lines = &lt;FILE&gt;; 即可解决此问题。

所以考虑到这一点:

#!/usr/bin/perl -T

use strict;
use warnings;

use CGI qw(:cgi-lib :standard);
print "Content-type: text/html\n\n";

my $input       = param('sequence');
my $meanexpfile = "final_expression_complete.txt";
open( my $input_file, "<", $meanexpfile ) or die "unable to open file";
my @meanmatches;

my @regex = ( split /\s/, $input );
my @lines = <$input_file>;
chomp (@lines);
close($input_file) or warn $!;

foreach my $regex (@regex) {
    foreach my $line (@lines) {
        if ( $line =~ m/$regex\s(.+\n)/i ) {
            push( @meanmatches, $1 );
        }
    }

    my $average                  = average(@meanmatches);
    my $std_dev                  = std_dev( $average, @meanmatches );
    my $average_round            = sprintf( "%0.4f", $average );
    my $stdev_round              = sprintf( "%0.4f", $std_dev );
    my $coefficient_of_variation = $stdev_round / $average_round;
    my $cv_round = sprintf( "%0.4f", $coefficient_of_variation );

    print font(
        { color => "blue" }, "<br><B>$regex average: $average_round   
&nbspStandard deviation: $stdev_round&nbspCoefficient of 
variation(Cv): $cv_round</B>"
    );
}

sub average {
    my (@values) = @_;
    my $count    = scalar @values;
    my $total    = 0;
    $total += $_ for @values;

    return $count ? $total / $count : 0;
}

sub std_dev {
    my ( $average, @values ) = @_;

    my $count       = scalar @values;
    my $std_dev_sum = 0;
    $std_dev_sum += ( $_ - $average )**2 for @values;

    return $count ? sqrt( $std_dev_sum / $count ) : 0;
}

【讨论】:

  • 信不信由你,我的初稿有这个,但我又丢了:)。
【解决方案2】:

这里的问题是,从 foreach 的第二次迭代开始,您试图从已读取的文件句柄中读取。您需要倒回到开头再读一遍:

foreach $regex (@regex) {
    seek FILE, 0, 0;
    while ( my $line = <FILE> ) {

但是,这看起来不是很高效。为什么要多次读取文件,当您可以在 foreach 开始之前读取一次,然后遍历列表:

my @lines;
while (<FILE>) {
    push (@lines, $_);
}

foreach $regex (@regex) {
    foreach $line (@lines) {

有了后者,您可能还需要考虑使用 grep 而不是 while 循环。

【讨论】:

  • 如果您所做的只是push将行添加到数组中,您可以删除while 循环并简单地执行my @lines = &lt;FILE&gt;; 不要忘记chomp!跨度>
猜你喜欢
  • 2023-04-11
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
  • 2013-10-26
  • 1970-01-01
  • 1970-01-01
  • 2013-10-01
  • 2023-03-02
相关资源
最近更新 更多