第24和26行的语法错误。我不知道为什么?

时间:2013-02-18 16:01:38

标签: perl bioinformatics

bioinfo2.pl第24行的

语法错误,靠近“);” bioinfo2.pl第26行的语法错误,靠近“}” 由于编译错误,bioinfo2.pl的执行中止。

print "Enter file name......\n\n";
chomp($samplefile = <STDIN>);
open(INFILE,"$samplefile") or die "Could not open $samplefile";

@residue_name= ();
@residue_count= ();
while($newline = <INFILE>) 
{
    if ($newline =~ /^ATOM/)
    {
        chomp $newline;
        @columns = split //, $newline;  
        $res = join '', $columns[17], $columns[18], $columns[19];
        splice @columns,0;
        $flag=0
        for ($i = 0; $i<scalar(@residue_name); $i++;) 
        {
            if (@residue_name[i] == $res)
            {
                @residue_count[i] = @residue_count[i] + 1;
                $flag=1;

            }
        }
        if($flag==0)
        {
            push(@residue_name, $res);  
        }

        for ($i = 0; $i<scalar(@residue_name); $i++) 
        {   
            print (@residue_name[i], "-------", @residue_count[i], "\n");
        }
    }   
}               

2 个答案:

答案 0 :(得分:2)

use strict; use warnings可能是明智之举。这迫使你声明你的变量(你可以使用my),并排除许多可能的错误。

以下是我注意到的一些事情:

  1. 在Perl5 v10及更高版本中,您可以使用say函数(use 5.010use feature 'say')。这与print类似,但最后添加了换行符。

  2. 永远不要使用两个arg形式的open。这会打开一些安全问题。提供明确的开放模式。此外,您可以使用标量作为文件句柄;这提供了很好的功能,如自动关闭文件。

    open my $INFILE, '<', $samplefile or die "Can't open $samplefile: $!";
    

    $!变量包含open失败的原因。

  3. 如果要从数组中检索元素列表,可以使用切片(多个下标):

    my $res = join '', @columns[17 .. 19]; # also, range operator ".."
    

    请注意,sigil现在是@,因为我们需要多个元素。

  4. splice @columns, 0是一种说法“删除数组中的所有元素并返回它们”的奇特方式。这不是必要的(您之后不会从该变量中读取)。如果使用词法变量(用my声明),则while循环的每次迭代都将接收一个新变量。如果您确实要删除内容,可以undef @columns。这应该更有效率。

  5. 实际错误:$flag = 0之后需要分号才能在开始循环之前终止语句。

  6. 实际错误: C样式的for循环包含parens中包含的三个表达式。你的最后一个分号将它们分成4个表达式,这是一个错误。只需将其删除,或查看我的下一个提示:

  7. C风格的循环(for (foo; bar; baz) {})很痛苦且容易出错。如果只迭代一个范围(例如索引),则可以使用范围运算符:

    for my $i (0 .. $#residue_name) { ... }
    

    $# sigil给出了数组的最后一个索引。

  8. 当订阅数组(访问数组元素)时,你必须包含索引的符号:

    $residue_name[$i]
    

    请注意,数组的符号为$,因为我们只能访问一个元素。

  9. 模式$var = $var + 1可缩短为$var++。这使用增量运算符。

  10. $flag == 0可缩写为!$flag,因为除零以外的所有数字均视为真。

  11. 这是脚本的重新实现。它将文件名作为命令行参数;这比提示用户更灵活。

    #!/usr/bin/perl
    
    use strict; use warnings; use 5.010;
    
    my $filename = $ARGV[0]; # @ARGV holds the command line args
    open my $fh, "<", $filename or die "Can't open $filename: $!";
    
    my @residue_name;
    my @residue_count;
    
    while(<$fh>) { # read into "$_" special variable
       next unless /^ATOM/; # start a new iteration if regex doesn't match
    
       my $number = join "", (split //)[17 .. 19]; # who needs temp variables?
    
       my $push_number = 1; # self-documenting variable names
       for my $i (0 .. $#residue_name) {
           if ($residue_name[$i] == $number) {
               $residue_count[$i]++;
               $push_number = 0;
           }
       }
       push @residue_name, $number if $push_number;
    
       # are you sure you want to print this after every input line?
       # I'd rather put this outside the loop.
       for my $i (0 .. $#residue_name) {
           say $residue_name[$i], ("-" x 7), $residue_count[$i]; # "x" repetition operator
       }
    } 
    

    这是一个对大型输入文件可能更快的实现:我们使用哈希(查找表),而不是循环遍历数组:

    #!/usr/bin/perl
    
    use strict; use warnings; use 5.010;
    
    my $filename = $ARGV[0]; # @ARGV holds the command line args
    open my $fh, "<", $filename or die "Can't open $filename: $!";
    
    my %count_residue; # this hash maps the numbers to counts
                       # automatically guarantees that every number has one count only
    
    while(<$fh>) { # read into "$_" special variable
       next unless /^ATOM/; # start a new iteration if regex doesn't match
    
       my $number = join "", (split //)[17 .. 19]; # who needs temp variables?
    
       if (exists $count_residue{$number}) {
         # if we already have an entry for that number, we increment:
         $count_residue{$number}++;
       } else {
         # We add the entry, and initialize to zero
         $count_residue{$number} = 0;
       }
       # The above if/else initializes new numbers (seen once) to zero.
       # If you want to count starting with one, replace the whole if/else by
       #     $count_residue{$number}++;
    
       # print out all registered residues in numerically ascending order.
       # If you want to sort them by their count, descending, then use
       #     sort { $count_residue{$b} <=> $count_residue{$a} } ...
       for my $num (sort {$a <=> $b} keys %count_residue) {
           say $num, ("-" x 7), $count_residue{$num};
       }
    } 
    

答案 1 :(得分:2)

我花了一段时间来解决所有各种错误。正如其他人所说,使用use warnings;use strict;

规则#1 :每当您看到指向完美线条的语法错误时,您应该始终查看之前的行是否缺少分号。你在$flag=0之后忘记了分号。

为了追踪所有问题,我已将您的代码重写为更多现代语法:

#! /usr/bin/env perl
use strict;
use warnings;
use autodie;

print "Enter file name......\n\n";
chomp (my $samplefile = <STDIN>);
open my $input_file, '<:crlf', $samplefile;

my @residue_name;
my @residue_count;
while ( my $newline = <$input_file> ) {
    chomp $newline;
    next if $newline !~ /^ATOM/;  #Eliminates the internal `if`
    my @columns = split //, $newline;
    my $res = join '', $columns[17], $columns[18], $columns[19];
    my $flag = 0;
    for my $i (0..$#residue_name) {
        if ( $residue_name[$i] == $res ) {
            $residue_count[$i]++;
            $flag = 1;
        }
    }
    if ( $flag == 0 ) {
        push @residue_name, $res;
    }

    for my $i (0..$#residue_name) {
        print "$residue_name[$i] -------  $residue_count[$i]\n";
    }
}
close $input_file;

以下是更改列表:

  • 第2行&amp; 3 :始终使用use strict;use warnings;。这些将帮助您追踪约90%的程序错误。
  • 第4行:使用use autodie;。这样就无需检查文件是否打开。
  • 第7行(和其他人):使用use strict;要求您预先声明变量。因此,无论何时首次使用变量,您都会看到my
  • 第8行:使用三个参数open并使用局部变量作为文件句柄而不是globs(即$ file_handle与FILE_HANDLE)。主要原因是局部变量比globs更容易传递到子程序中。
  • 第9行&amp; 10 :无需初始化数组,只需声明它们即可。
  • 第13行:您在阅读后始终chomp
  • 第14行:这样做可以消除整个内部if语句,它包含整个while循环。代码块(例如ifwhilefor)很难弄清楚它们是否太长而且太多嵌入彼此内部。以这种方式使用next可以消除if阻止。
  • 第17行:这是您错过了分号的地方,它为您提供了第一个语法错误。最重要的是我消除了令人困惑的splice命令。如果你想将阵列归零,你可以简单地说@columns = ();更清楚。但是,由于@columns现在只在while循环中,因此我不再需要将其删除,因为它会为文件的每一行重新定义。
  • 第18行:这是一种更清晰的循环遍历数组所有行的方法。请注意,$#residue_name为您提供$#residue_name的最后一个索引,而标量@resudue_name为您提供了元素数量。这是一个非常重要的区别!如果我有@array = (0, 1, 2, 3, 4),则$#array将为4,但scalar @array将为5.使用C样式for循环可能会有点混乱这样做的时候。您应该使用>还是>=?使用(0..$#residue)名称是显而易见的,并消除了错误的可能性,其中包括C样式for语句中的额外分号。由于错误的可能性和语法的复杂性,创建Python的开发人员决定不允许循环使用C样式。
  • 第19行(和其他人):使用warnings指出您执行了@residue_name[i]并且它有几个问题。首先,在索引数组时应使用$residue_name[...],其次,i不是整数。你的意思是$i。因此@residue_name[i]变为$residue_name[$i]
  • 第20行:如果您要增加变量,请使用$foo++;$foo += 1;而不是$foo = $foo + 1;。前两个使得更容易看到你正在递增一个变量而不是重新计算它的值。
  • 第29行:Perl的一个重要功能是可以在引号内插入变量。您可以将所有内容放在一组引号中。顺便说一句,如果您将.语句分成多个部分,则应使用,而不是print,是列表操作。这意味着您打印出来的内容取决于$,的值。 $,是一个Perl变量,用于说明在将列表插入字符串时要在列表的每个项目之间打印的内容。

请不要将此视为对编码能力的批评。许多教Perl的Perl书籍,以及教授Perl的许多课程似乎都教Perl,因为它回归到Perl 3.0天。当我第一次学习Perl时,它是在Perl 3.0中,我的大部分语法都看起来像你的。但是,Perl 5.x已经推出了很长一段时间,并且包含许多功能,使编程更容易阅读。

我花了一些时间才摆脱Perl 3.0的习惯,进入Perl 4.0以及后来的Perl 5.0习惯。您可以通过查看其他人的工作来学习,并在Stack Overflow等论坛上提问。

我仍然不能说你的代码会起作用。我没有你的意见,所以我无法对此进行测试。但是,通过使用此代码作为程序的基础,调试这些错误应该非常简单。