【问题标题】:Perl Use of uninitialized value in addition when all values are initializedPerl 在所有值都初始化时使用未初始化的值
【发布时间】:2016-09-25 04:24:23
【问题描述】:

我正在尝试编写一个脚本来计算几个碳氢键的顺序参数并输出这些值。数学是微不足道的,但是当我尝试对最后的值进行平均时,我得到了“另外使用未初始化的值”错误。 我很清楚这个错误是多么常见和容易修复,但我检查了所有给定的值,所有 9212 个值都有一个值(我通过打印每个值进行检查,将其放入 excel 文档并搜索空单元格)。我很茫然,我不知道如何进一步调试。

我的脚本接受一个输入文件,逐行执行,如果存在某些字符串,则采用 x、y、z 坐标,对这些坐标进行数学运算(找到两个向量与 z 轴之间的角度),应该是将每个 $integer 部分平均在一起(因此所有 2 的平均值等)。它对 3 个段(2-8、9-10 和 11-18)执行此操作,将它们保存到两个数组(@theta_values 和 @theta2_values),最后它应该将每个“整数”平均在一起以找到矢量和 z 轴。 总共应该有 34 个值输出,这确实发生了,但每个值都有一个“在 angle_checker_v3.pl 第 334 行,第 34303 行使用未初始化的值(+)”。误差,并且除第一个以外的所有平均值都太小了。

作为参考,第 334 行是我平均的位置,第 34303 行是文件的最后一行。

一些示例数据是:

ATOM   2199  C22 POPC    1      -9.427  11.863  11.706  1.00  0.00      MEMB
ATOM   2200  H2R POPC    1     -10.347  11.662  12.293  1.00  0.00      MEMB
ATOM   2201  H2S POPC    1      -8.968  10.895  11.443  1.00  0.00      MEMB
ATOM   2211  C23 POPC    1      -9.801  12.641  10.423  1.00  0.00      MEMB
ATOM   2212  H3R POPC    1     -10.136  13.667  10.696  1.00  0.00      MEMB
ATOM   2213  H3S POPC    1     -10.658  12.124   9.934  1.00  0.00      MEMB
ATOM   2214  C24 POPC    1      -8.663  12.751   9.396  1.00  0.00      MEMB
ATOM   2215  H4R POPC    1      -7.763  13.166   9.894  1.00  0.00      MEMB
ATOM   2216  H4S POPC    1      -8.961  13.479   8.607  1.00  0.00      MEMB

*我故意跳过上面的 10 个无关紧要的原子

按列表示的顺序:物质(不相关)、原子编号、原子类型、残基编号/分子类型、残基编号、x 坐标、y 坐标、z 坐标、α 编号(不相关)、 beta 列(不相关)和整体分子类型。

TLDR; 我的平均脚本:

#Averaging theta values
for (my $t=2; ($t <= 18); $t++) {
    for (my $j=1; ($j <= $lipid_num); $j++) {
            $sum[$t]= $theta_values[$t][$j] + $sum[$t];
    }
    $average[$t]= $sum[$t] / $lipid_num;
    print "Average theta for carbon $t is $average[$t]\n";
}

#Averaging Theta2 values
for (my $q=2; ($q <= 18); $q++) {
        for (my $b=1; ($b <= $lipid_num); $b++) {
                $sum2[$q]= $theta2_values[$q][$b] + $sum2[$q];
        }
        $average2[$q]= $sum2[$q] / $lipid_num;
        print "Average theta2 for carbon $q is $average2[$q]\n";
}

即使我已验证所有位置都有值,但在所有位置都没有找到值。

这是完整的脚本,我知道它有多大。

        #Usage:                                                                      #
# perl angle_checker.pl [granuphilin_prot-memb_system].pdb 
#!/usr/bin/perl 

use strict;
use warnings;
use Math::Trig;

my $inputfile = $ARGV[0];

open (INPUTFILE, "<", $inputfile) or die $!;

my @data = <INPUTFILE>;

#Quick Change Variables

my $lipid_num = 256;

#Library
my @sum;
my @average;
my @sum2;
my @average2;
my @x1;
my @y1;
my @z1;
my $R = 'R';
my $S = 'S';
my $one = '1';
my @theta_values;
my @theta2_values;
my @vectorCtoHR;
my @vectorCtoHS;
my @normal;

#Start for lipid count
for (my $lipid=1; ($lipid <= $lipid_num); $lipid++) {
    # First Carbon/Integer counter
    for (my $integer= 2; ($integer <= 8); $integer++) {
            #Split line 1
            for (my $line = 0; $line <= $#data; ++$line) {
                    #Search 1.1
                    if(($data[$line] =~ m/\s+C2$integer\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                            chomp $data[$line];
                            my @splitline = (split /\s+/, $data[$line]);
                            foreach (@splitline) {
                                    $x1[0]= $splitline[5];
                                    $y1[0]= $splitline[6];
                                    $z1[0]= $splitline[7];
                            }
                    }
                    #Search 1.2
                        if(($data[$line] =~ m/\s+H$integer$R\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                                my @splitline = (split /\s+/, $data[$line]);
                            foreach (@splitline) {
                                    $x1[1]= $splitline[5];
                                        $y1[1]= $splitline[6];
                                        $z1[1]= $splitline[7];
                            }
                    }
                    #Search 1.3
                        if(($data[$line] =~ m/\s+H$integer$S\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                                my @splitline = (split /\s+/, $data[$line]);
                            foreach (@splitline) {
                                    $x1[2]= $splitline[5];
                                        $y1[2]= $splitline[6];
                                        $z1[2]= $splitline[7];
                            }
                    }
            }



    #Z-axis
    $normal[0]= 0;
    $normal[1]= 0;
    $normal[2]= 100;

    #Vector 1
    $vectorCtoHR[0]=($x1[0] - ($x1[1]));
    $vectorCtoHR[1]=($y1[0] - ($y1[1]));
    $vectorCtoHR[2]=($z1[0] - ($z1[1]));

    #Vector 2
    $vectorCtoHS[0]=($x1[0] - ($x1[2]));
        $vectorCtoHS[1]=($y1[0] - ($y1[2]));
        $vectorCtoHS[2]=($z1[0] - ($z1[2]));

    #First Angle

    my $x1mag = sqrt(($vectorCtoHS[0]**2)+($vectorCtoHS[1]**2)+($vectorCtoHS[2]**2));
    my $x2mag = sqrt(($normal[0]**2)+($normal[1]**2)+($normal[2]**2));

    #Dot product 
    my $dotproduct = (($vectorCtoHS[0]*$normal[0])+($vectorCtoHS[1]*$normal[1])+($vectorCtoHS[2]*$normal[2]));

    my $theta = acos($dotproduct/($x1mag*$x2mag));
    $theta_values[$integer][$lipid]= $theta;

    # Second Angle  
        my $x3mag = sqrt(($vectorCtoHR[0]**2)+($vectorCtoHR[1]**2)+($vectorCtoHR[2]**2));

        my $dotproduct2 = (($vectorCtoHR[0]*$normal[0])+($vectorCtoHR[1]*$normal[1])+($vectorCtoHR[2]*$normal[2]));

        my $theta2 = acos($dotproduct2/($x3mag*$x2mag));
        $theta2_values[$integer][$lipid]= $theta2;
    }
    #Section 2 Search These only have one hydrogen to search for, hence 1 less search
    for (my $integer = 9; ($integer <= 10); $integer++) {
                for (my $line = 0; $line <= $#data; ++$line) {
                        if(($data[$line] =~ m/\s+C2$integer\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                                chomp $data[$line];
                                my @splitline = (split /\s+/, $data[$line]);
                                foreach (@splitline) {
                                        $x1[0]= $splitline[5];
                                        $y1[0]= $splitline[6];
                                        $z1[0]= $splitline[7];
                                }
                        }
                    if(($data[$line] =~ m/\s+H$integer$one\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                                my @splitline = (split /\s+/, $data[$line]);
                                foreach (@splitline) {
                                        $x1[1]= $splitline[5];
                                        $y1[1]= $splitline[6];
                                        $z1[1]= $splitline[7];
                                }
                        }
                }
    $normal[0]= 0;
    $normal[1]= 0;
    $normal[2]= 100;
    $vectorCtoHR[0]=($x1[0] - ($x1[1]));
    $vectorCtoHR[1]=($y1[0] - ($y1[1]));
    $vectorCtoHR[2]=($z1[0] - ($z1[1]));

    my $x1mag = sqrt(($vectorCtoHR[0]**2)+($vectorCtoHR[1]**2)+($vectorCtoHR[2]**2));
    my $x2mag = sqrt(($normal[0]**2)+($normal[1]**2)+($normal[2]**2));

    #Dot product 
    my $dotproduct = (($vectorCtoHR[0]*$normal[0])+($vectorCtoHR[1]*$normal[1])+($vectorCtoHR[2]*$normal[2]));

    my $theta = acos($dotproduct/($x1mag*$x2mag));
    $theta_values[$integer][$lipid]= $theta;
    $theta2_values[$integer][$lipid]= $theta;
    }

    #Effectively the same as section 1
    for (my $integer= 11; ($integer <= 18); $integer++) {
            for (my $line = 0; $line <= $#data; ++$line) {
                    if(($data[$line] =~ m/\s+C2$integer\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                            chomp $data[$line];
                            my @splitline = (split /\s+/, $data[$line]);
                            foreach (@splitline) {
                                    $x1[0]= $splitline[5];
                                    $y1[0]= $splitline[6];
                                    $z1[0]= $splitline[7];
                            }
                    }
                        if(($data[$line] =~ m/\s+H$integer$R\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                                my @splitline = (split /\s+/, $data[$line]);
                            foreach (@splitline) {
                                    $x1[1]= $splitline[5];
                                        $y1[1]= $splitline[6];
                                        $z1[1]= $splitline[7];
                            }
                    }
                        if(($data[$line] =~ m/\s+H$integer$S\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                                my @splitline = (split /\s+/, $data[$line]);
                            foreach (@splitline) {
                                    $x1[2]= $splitline[5];
                                        $y1[2]= $splitline[6];
                                        $z1[2]= $splitline[7];
                            }
                    }
            }
    $normal[0]= 0;
    $normal[1]= 0;
    $normal[2]= 100;

    $vectorCtoHR[0]=($x1[0] - ($x1[1]));
    $vectorCtoHR[1]=($y1[0] - ($y1[1]));
    $vectorCtoHR[2]=($z1[0] - ($z1[1]));

    $vectorCtoHS[0]=($x1[0] - ($x1[2]));
        $vectorCtoHS[1]=($y1[0] - ($y1[2]));
        $vectorCtoHS[2]=($z1[0] - ($z1[2]));

    #First Angle

    my $x1mag = sqrt(($vectorCtoHS[0]**2)+($vectorCtoHS[1]**2)+($vectorCtoHS[2]**2));
    my $x2mag = sqrt(($normal[0]**2)+($normal[1]**2)+($normal[2]**2));

    #Dot product 
    my $dotproduct = (($vectorCtoHS[0]*$normal[0])+($vectorCtoHS[1]*$normal[1])+($vectorCtoHS[2]*$normal[2]));

    my $theta = acos($dotproduct/($x1mag*$x2mag));
    $theta_values[$integer][$lipid]= $theta;
    }
print "done with $lipid\n";
#End of lipid search
}
#Averaging starts now

#Averaging theta values
for (my $t=2; ($t <= 18); $t++) {
    for (my $j=1; ($j <= $lipid_num); $j++) {
            $sum[$t]= $theta_values[$t][$j] + $sum[$t];
    }
    $average[$t]= $sum[$t] / $lipid_num;
    print "Average theta for carbon $t is $average[$t]\n";
}

#Averaging Theta2 values
for (my $q=2; ($q <= 18); $q++) {
        for (my $b=1; ($b <= $lipid_num); $b++) {
                $sum2[$q]= $theta2_values[$q][$b] + $sum2[$q];
        }
        $average2[$q]= $sum2[$q] / $lipid_num;
        print "Average theta2 for carbon $q is $average2[$q]\n";
}

【问题讨论】:

  • 是否可以在某处发布输入数据文件?没有它,问题就无法在本地重现,从而使调试变得困难一个数量级。
  • 尝试先将@sum@sum2 数组初始化为零?
  • 另外,您将 $q 从 2 迭代到 18。您的意思是跳过 @sum@sum2 数组的 0 和 1 位置吗?除非您对此采取措施,否则这些将不会被初始化。
  • 您的代码也将受益于将您几乎重复的代码部分重构为子例程。 $integer 的任意索引也应转换为常量,并为其值提供一些理由。在你的代码的 cmets 中输入格式的一个例子也会为其他试图理解你的代码的人创造奇迹。
  • 第一个观察:这是太多的直接代码。它应该分成子程序。然后您可以实际检查该过程的各个部分。此外,它会允许并迫使您对问题进行分区和结构化,几乎可以肯定会产生更好的代码。

标签: perl


【解决方案1】:

如果没有输入数据,就无法在本地重现问题,从而几乎无法帮助调试。不过,在查看了您的代码后,我可以提出一些建议来简化代码并希望更容易找到问题。

首先,几乎所有的循环都在 C 风格的 for 循环中的两个值之间迭代一个整数变量。如果您绝对需要,for 的形式就在那里,但 perl 的表现力要强得多——因此更容易阅读和理解作者的意图——for 循环的形式。

您只需要一个整数范围;例如

for (my $integer= 2; ($integer <= 8); $integer++) { 

您可以简单地说“我希望 $integer 从 2 变为 8”;

for my $integer (2 .. 8)

如果您仅将整数用于索引回数组以获取内容,您可以简单地告诉 perl 您想要遍历数组内容 - 即,而不是;

for (my $line = 0; $line <= $#data; ++$line) {
    if(($data[$line] =~ ... etc ...
    chomp $data[$line];

你可以更简单;

for my $line (@data) {
    if(($line =~ ... etc ...
    chomp $line;

其次,如果您使用了多个正则表达式,则有助于将它们的定义与它们的使用区分开来。它允许读者单独使用和理解正则表达式本身,而不是(稍后)查看它是如何/为什么被应用的。此外,“扩展模式”正则表达式允许在正则表达式定义中使用空格。令人惊讶的是,阅读带有空格的正则表达式(regexen?)变得多么容易 - 如此之多,以至于人们应该考虑将其作为规则,简单地始终使用/x。我们可以一起替换;

if(($data[$line] =~ m/\s+C2$integer\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {

与;

my $has_POPC        = qr/ \s+  POPC        \s+ /x;
my $has_lipid       = qr/ \s+  $lipid      \s+ /x;
my $has_C2_integer  = qr/ \s+  C2 $integer \s+ /x;

if( $line =~ $has_C2_integer  &&  $line =~ $has_lipid  &&  $line =~ $has_POPC) {

第三——也许最重要的是因为我认为它可能是一个错误——在你的几个内部循环中;

my @splitline = (split /\s+/, $data[$line]);
foreach (@splitline) {
    $x1[0]= $splitline[5];
    $y1[0]= $splitline[6];
    $z1[0]= $splitline[7];
}

再次 - 我没有输入数据,因此无法检查 - 但这几乎可以肯定是一个错误。你在空格上分割线 - 为了讨论,假设它有 10 个“片段”。然后迭代这些片段(将它们放入默认主题$_),但不引用主题片段本身——即,您没有使用$_。因此,代码将 5、6 和 7 块(分别)放入 x1、y1 和 z1 - 10 倍以上。现在,它可能无关紧要,但正如我所说,它几乎肯定不是你想要的,因此是一个等待发生的错误。您可能(这是简洁性与可读性之间的平衡问题)希望将三个赋值合并为列表形式并(再次,可选地)消除临时变量@splitline

if( $line =~ $has_C2_integer  &&  $line =~ $has_lipid  &&  $line =~ $has_POPC) {
    ( $x1[0], $y1[0], $z1[0] ) = (split /\s+/ $line)[ 5, 6, 7 ];
}

将这些想法放在一起可以替代它;

#Start for lipid count
for (my $lipid=1; ($lipid <= $lipid_num); $lipid++) {
    # First Carbon/Integer counter
    for (my $integer= 2; ($integer <= 8); $integer++) {
        #Split line 1
        for (my $line = 0; $line <= $#data; ++$line) {
                #Search 1.1
                if(($data[$line] =~ m/\s+C2$integer\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                        chomp $data[$line];
                        my @splitline = (split /\s+/, $data[$line]);
                        foreach (@splitline) {
                                $x1[0]= $splitline[5];
                                $y1[0]= $splitline[6];
                                $z1[0]= $splitline[7];
                        }
                }
                #Search 1.2
                    if(($data[$line] =~ m/\s+H$integer$R\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                            my @splitline = (split /\s+/, $data[$line]);
                        foreach (@splitline) {
                                $x1[1]= $splitline[5];
                                    $y1[1]= $splitline[6];
                                    $z1[1]= $splitline[7];
                        }
                }
                #Search 1.3
                    if(($data[$line] =~ m/\s+H$integer$S\s+/)&&($data[$line] =~ m/\s+$lipid\s+/)&&($data[$line] =~ m/\s+POPC\s+/)) {
                            my @splitline = (split /\s+/, $data[$line]);
                        foreach (@splitline) {
                                $x1[2]= $splitline[5];
                                    $y1[2]= $splitline[6];
                                    $z1[2]= $splitline[7];
                        }
                }
        }

用;

my $has_POPC = qr/ \s+ POPC \s+ /x;
#Start for lipid count
for my $lipid (1 .. $lipid_num) {
    my $has_lipid = qr/ \s+ $lipid \s+ /x;

    # First Carbon/Integer counter
    for my $integer (2 .. 8) {
        my $has_C2_integer  = qr/ \s+ C2 $integer   \s+ /x;
        my $has_H_integer_R = qr/ \s+ H  $integer R \s+ /x;
        my $has_H_integer_S = qr/ \s+ H  $integer S \s+ /x;

        #Split line 1
        for my $line (@data) {
            chomp $line;

            #Search 1.1
            if ($line =~ $has_C2_integer && $line =~ $has_lipid && $line =~ $has_POPC)  {
                ($x1[0], $y1[0], $z1[0]) = (split /\s+/ $line)[ 5, 6, 7 ];
            }

            #Search 1.2
            if ($line =~ $has_H_integer_R && $line =~ $has_lipid && $line =~ $has_POPC) {
                ($x1[1], $y1[1], $z1[1]) = (split /\s+/ $line)[ 5, 6, 7 ];
            }

            #Search 1.3
            if ($line =~ $has_H_integer_S && $line =~ $has_lipid && $line =~ $has_POPC) {
                ($x1[2], $y1[2], $z1[2]) = (split /\s+/ $line)[ 5, 6, 7 ];
            }
        }
    }
}

...这将行数减少了大约三分之一,并且(可以说)更具可读性。稍后在程序中结构几乎逐字重复,因此您应该能够再次获得相同的归约。当然,这必须严格检查,这是我做不到的。最后,好好看看Perl debugger。只需大约 15 分钟即可完成基础知识,并且会(至少)回报您 10 倍。

【讨论】:

    【解决方案2】:

    原来我正在将没有值的 $sum[$t] 添加到执行的操作中,这导致了错误。为了解决这个问题,我改变了:

    $sum[$t]= $theta_values[$t][$j] + $sum[$t];
    

    收件人:

    $sum[$t]+= $theta_values[$t][$j];
    

    感谢大家的帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-19
      • 1970-01-01
      • 2013-04-08
      • 1970-01-01
      • 2014-02-05
      • 2015-06-03
      • 1970-01-01
      • 2012-06-20
      相关资源
      最近更新 更多