我会做这样
use strict;
use warnings;
sub get_ca_atom {
my @result = split;
return
$result[0] eq 'ATOM'
&& $result[2] eq 'CA'
&& @result > 8 ? [ @result[ 5 .. 8 ] ] :();
}
my @atoms = map get_ca_atom, <>;
while (@atoms) {
my $a = shift @atoms;
for my $b (@atoms) {
my $dist
= sqrt(($$a[1] - $$b[1])**2
+ ($$a[2] - $$b[2])**2
+ ($$a[3] - $$b[3])**2);
printf "%s\t%s\t%0.3f\n", $$a[0], $$b[0], $dist;
}
}
但在现实中我想单独从主算法的原子内部处理,以明确并传达尽可能好的代码的意图。没有什么比在没有牢记这一点的情况下编写几个月后处理自己的代码更糟。
use strict;
use warnings;
# handle CA ATOM record
use constant { ATOM_TAG => 0, ATOM_TYPE => 2 };
use constant ATOM_SPLICE => (5 .. 8);
use constant { NAME => 0, X => 1, Y => 2, Z => 3 };
sub get_ca_atom {
my @result = split;
return
$result[ATOM_TAG] eq 'ATOM'
&& $result[ATOM_TYPE] eq 'CA'
&& @result > (ATOM_SPLICE)[-1] ? [ @result[ATOM_SPLICE] ] :();
}
sub get_name { shift->[NAME] }
sub distance {
my ($a, $b) = @_;
sqrt( ($$a[X] - $$b[X])**2
+ ($$a[Y] - $$b[Y])**2
+ ($$a[Z] - $$b[Z])**2);
}
# end of handle CA ATOM record
my @atoms = map get_ca_atom, <>;
while (@atoms) {
my $a = shift @atoms;
for my $b (@atoms) {
my $dist = distance($a, $b);
printf "%s\t%s\t%0.3f\n", get_name($a), get_name($b), $dist;
}
}
然后,您可以随意使用主算法。例如,上面的代码会将所有文件内容读入内存,这在大多数情况下都不是真正的问题。但是,如果您只想保留CA ATOMS,只需将map
更改为以下行。
my @atoms;
while (<>) {
my $atom = get_ca_atom;
push @atoms, $atom if $atom;
}
正如你看到的,代码的意图慢慢地多行失踪,但也可以是相反的时候。对代码的主要反对意见是沟通意图,即使这意味着更多的代码行。特别是你不应该混合低水平和高水平,这就是我在第二个代码示例中将distance
和get_name
分开的原因。
如果您更愿意按原子顺序处理原子,则可以使用以下代码,但请注意,由于无论如何需要存储@atoms
来计算距离,因此请注意不会节省内存。
my @atoms;
while (<>) {
my $atom = get_ca_atom;
next unless $atom;
for my $a (@atoms) {
my $dist = distance($atom, $a);
printf "%s\t%s\t%0.3f\n", get_name($a), get_name($atom), $dist;
}
push @atoms, $atom;
}
注输出顺序不同。并且还注意到我使用了next unless $atom;
而不是使用if ($atom) {
,并将块的其余部分封闭起来。原因是我想强调:只要不是你所期望的就跳过它。如果您想用第三种不同的输出顺序让您感到惊喜,您可以用unshift
替换push
。
你的代码在如此多的层面上是错误的,我甚至不知道从哪里开始。 '$ argv'是什么?为什么把你的输入放在'@ ARGV'中?你认为'@ temp'内容在下一个if语句中改变了吗?你认为更少的线路更好?你知道你想要输出1653个距离吗? – 2015-03-19 06:40:34
错误地我粘贴了另一个文件,我正在使用的实际文件不包含'$ argv'。现在'@ ARGV'用于命令行执行。 **是在下一个@tmp中的内容如果没有改变(主要问题)**。我不知道代码中行数越少越好,但我需要在上面的代码中添加另一个计算部分,这就是为什么我提到过。 – Bionerd 2015-03-19 06:41:48
如果您要为_command line execution_使用'@ ARGV',则代码'@ARGV = <>;'不是您要查找的内容。它偶然工作,并且让你的代码读者感到困惑。顺便说一句,代码应该首先与人交流,即使他们都是你,否则你可以使用机器代码。 – 2015-03-19 06:53:40