这可能是可取的use strict; use warnings
。这迫使你声明你的变量(你可以这样做my
),并排除了许多可能的错误。
以下是我注意到的几件事:
在 Perl5 v10 及更高版本中,您可以使用say
函数 (use 5.010
或use feature 'say'
)。这就像print
但在最后添加了一个换行符。
永远不要使用双参数形式的 open。这会引发一些安全问题。提供明确的开放模式。此外,您可以使用标量作为文件句柄;这提供了很好的功能,例如自动关闭文件。
open my $INFILE, '<', $samplefile or die "Can't open $samplefile: $!";
该变量包含失败$!
的原因。open
如果要从数组中检索元素列表,可以使用切片(多个下标):
my $res = join '', @columns[17 .. 19]; # also, range operator ".."
请注意,印记现在是@
,因为我们采用多个元素。
这splice @columns, 0
是“从数组中删除所有元素并返回它们”的一种奇特方式。这不是必需的(稍后您不会从该变量中读取)。如果您使用词法变量(用 声明my
),那么循环的每次迭代while
都会收到一个新变量。如果你真的想删除内容,你可以undef @columns
. 这应该更有效率。
实际错误:您需要一个分号 after$flag = 0
来终止语句,然后才能开始循环。
实际错误: C 风格的 for 循环包含括号中的三个表达式。您的最后一个分号将它们分成 4 个表达式,这是一个错误。只需将其删除,或查看我的下一个提示:
C 风格的循环 ( for (foo; bar; baz) {}
) 很痛苦且容易出错。如果您只迭代一个范围(例如索引),那么您可以使用范围运算符:
for my $i (0 .. $#residue_name) { ... }
$#
印记给出了数组的最后一个索引。
下标数组(访问数组元素)时,您必须包含索引的印记:
$residue_name[$i]
请注意,数组的标志是$
,因为我们只访问一个元素。
图案$var = $var + 1
可以缩短为$var++
. 这使用增量运算符。
$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};
}
}