0

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");
        }
    }   
}               
4

2 回答 2

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 行和第 3 行:始终使用use strict;use warnings;。这些将帮助您追踪大约 90% 的程序错误。
  • 第 4 行:使用use autodie;. 这将消除检查文件是否打开的需要。
  • 第 7 行(和其他):使用use strict;要求您预先声明变量。my因此,无论何时首次使用变量,您都会看到。
  • 第 8 行:使用三个参数open并使用本地变量作为文件句柄而不是 glob(即 $file_handle 与 FILE_HANDLE)。主要原因是局部变量比 glob 更容易传递到子例程中。
  • 第 9 行和第 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 循环可能会有点混乱。你应该使用>or>=吗?使用名称是显而易见的,并且消除了在 C 样式语句(0..$#residue)中包含额外分号的错误的可能性。for由于出错的可能性和语法的复杂性,创建 Python 的开发人员决定不允许 C 风格的 for 循环。
  • 第 19 行(和其他):Usingwarnings指出你做了@residue_name[i],但它有几个问题。首先,$residue_name[...]索引数组时应该使用,其次,i不是整数。你的意思是$i。从而@residue_name[i]变成$residue_name[$i]
  • 第 20 行:如果要增加变量,请使用$foo++;or$foo += 1;和 not $foo = $foo + 1;。前两个使您更容易看到您正在递增变量而不是重新计算它的值。
  • 第 29 行:Perl 的一大特点是可以在引号内插入变量。您可以将所有内容放在一组引号内。顺便说一句,如果你确实将一个语句分成多个部分,你应该使用.而不是。这是一个列表操作。这意味着您打印的内容取决于. 这是一个 Perl 变量,它说明当您将列表插入字符串时,在列表的每个项目之间打印什么。,print,$,$,

请不要将此视为对您编码能力的批评。许多教授 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 等论坛上提问来学习。

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

于 2013-02-18T18:41:51.213 回答
2

这可能是可取的use strict; use warnings。这迫使你声明你的变量(你可以这样做my),并排除了许多可能的错误。

以下是我注意到的几件事:

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

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

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

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

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

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

    请注意,印记现在是@,因为我们采用多个元素。

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

  5. 实际错误:您需要一个分号 after$flag = 0来终止语句,然后才能开始循环。

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

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

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

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

  8. 下标数组(访问数组元素)时,您必须包含索引的印记:

    $residue_name[$i]
    

    请注意,数组的标志是$,因为我们只访问一个元素。

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

  10. $flag == 0可以缩写为!$flag,因为除零之外的所有数字都被认为是正确的。

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

#!/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};
   }
} 
于 2013-02-18T17:33:29.573 回答