如何在Perl中重写此代码中的一行代码(或命令行中的更少行代码)?

时间:2011-03-23 08:10:23

标签: perl code-golf

我有这样的代码:

#!/usr/bin/perl
use strict;
use warnings;      
my %proteins = qw/
    UUU F UUC F UUA L UUG L UCU S UCC S UCA S UCG S UAU Y UAC Y UGU C UGC C UGG W
    CUU L CUC L CUA L CUG L CCU P CCC P CCA P CCG P CAU H CAC H CAA Q CAG Q CGU R CGC R CGA R CGG R
    AUU I AUC I AUA I AUG M ACU T ACC T ACA T ACG T AAU N AAC N AAA K AAG K AGU S AGC S AGA R AGG R
    GUU V GUC V GUA V GUG V GCU A GCC A GCA A GCG A GAU D GAC D GAA E GAG E GGU G GGC G GGA G GGG G
    /;
open(INPUT,"<dna.txt");
while (<INPUT>) {    
    tr/[a,c,g,t]/[A,C,G,T]/;
    y/GCTA/CGAU/;    
    foreach my $protein (/(...)/g) {
        if (defined $proteins{$protein}) {
        print $proteins{$protein};
        }
}
}
close(INPUT);

此代码与我的其他问题的答案有关:DNA to RNA and Getting Proteins with Perl

该计划的输出是:

SIMQNISGREAT

如何使用Perl重写该代码,它将在命令行上运行,并且将用更少的代码(如果可能的话,一行代码)重写?

PS 1: dna.txt就是这样:

TCATAATACGTTTTGTATTCGCCAGCGCTTCGGTGT

PS 2:如果代码的行数较少,则可以接受将my %proteins变量写入文件。

5 个答案:

答案 0 :(得分:3)

我建议的唯一更改是简化while循环:

while (<INPUT>) {
    tr/acgt/ACGT/;
    tr/GCTA/CGAU/;
    foreach my $protein (/(...)/g) {
        if (defined $proteins{$protein}) {
            print $proteins{$protein};
        }
    }
}

由于ytr是同义词,因此您应该只使用其中一个。我认为tr读取的内容比y更好,所以我选择了tr。此外,你用非常不同的方式调用它们,但这应该是相同的效果,只提到你实际改变的字母。 (所有其他角色都被转换为自己。这使得很多更难以看到实际被改变的内容。)

您可能希望删除open(INPUT,"<dna.txt");和相应的close(INPUT);行,因为它们使得在shell管道中使用您的程序或使用不同的输入文件更加困难。但这取决于你,如果输入文件总是dna.txt而且从来没有任何不同,那就没关系。

答案 1 :(得分:2)

#!/usr/bin/perl
%p=qw/UUU F UUC F UUA L UUG L UCU S UCC S UCA S UCG S UAU Y UAC Y UGU C UGC C UGG W
CUU L CUC L CUA L CUG L CCU P CCC P CCA P CCG P CAU H CAC H CAA Q CAG Q CGU R CGC R CGA R CGG R
AUU I AUC I AUA I AUG M ACU T ACC T ACA T ACG T AAU N AAC N AAA K AAG K AGU S AGC S AGA R AGG R
GUU V GUC V GUA V GUG V GCU A GCC A GCA A GCG A GAU D GAC D GAA E GAG E GGU G GGC G GGA G GGG G/;
$_=uc<DATA>;y/GCTA/CGAU/;map{print if$_=$p{$_}}/(...)/g
__DATA__
TCATAATACGTTTTGTATTCGCCAGCGCTTCGGTGT

呼。我能想出最好的,至少这个很快。如果您确定输入始终为大写,则还可以删除uc另外两个字符。或者,如果输入始终相同,您可以立即将其分配给$_,而不是从任何地方读取。

我想我不需要说这段代码应该在生产环境中使用,或者除了纯粹的乐趣以外的任何其他地方。在进行实际编程时,可读性几乎总是胜过紧凑性。

我在评论中提到的其他一些版本:

从文件中读取%p和DNA:

#!/usr/bin/perl
open A,"<p.txt";map{map{/(...)/;$p{$1}=chop}/(... .)/g}<A>;
open B,"<dna.txt";$_=uc<B>;y/GCTA/CGAU/;map{print if$_=$p{$_}}/(...)/g

来自perl -e的shell:

perl -e 'open A,"<p.txt";map{map{/(...)/;$p{$1}=chop}/(... .)/g}<A>;open B,"<dna.txt";$_=uc<B>;y/GCTA/CGAU/;map{print if$_=$p{$_}}/(...)/g'

答案 2 :(得分:2)

某人(@kamaci)在另一个帖子中叫了我的名字。这是我在将蛋白质表保持在命令行时能够想到的最好的结果:

perl -nE'say+map+substr("FYVDINLHL%VEMKLQL%VEIKLQFYVDINLHCSGASTRPWSGARTRP%SGARTRPCSGASTR",(s/GGG/GGC/i,vec($_,0,32)&101058048)%63,1),/.../g' dna.txt

(Shell引用,Windows引用交换'"个字符)。此版本标记了%的无效密码子,您可以通过在适当的位置添加=~y/%//d来解决此问题。

提示:这从RNA三元组的原始ASCII编码中选出6位,在0到101058048之间给出64个代码;为了得到一个字符串索引,我减少了模63的结果,但这创建了一个双映射,令人遗憾地必须编写两个不同的蛋白质。 s/GGG/GGC/i将其中一个映射到编码正确蛋白质的另一个。

另请注意%运算符之前的括号,两者,运算符与substr 的参数列表隔离开来修复& vs %的优先顺序。如果您曾在生产代码中使用它,那么您就是一个坏人,坏人。

答案 3 :(得分:1)

大多数事情已经被指出,特别是可读性很重要。我不会尝试减少程序,而不是以下内容。

use strict;
use warnings;
# http://stackoverflow.com/questions/5402405/
my $fnprot = shift || 'proteins.txt';
my $fndna  = shift || 'dna.txt';
# build protein table
open my $fhprot, '<', $fnprot or die "open $fnprot: $!";
my %proteins = split /\s+/, do { local $/; <$fhprot> };
close $fhprot;
# process dna data
my @result;
open my $fhdna, '<', $fndna or die "open $fndna: $!";
while (<$fhdna>) {
    tr/acgt/ACGT/;
    tr/GCTA/CGAU/;
    push @result, map $proteins{$_}, grep defined $proteins{$_}, m/(...)/g;
}
close $fhdna;
# check correctness of result (given input as per original post)
my $expected = 'SIMQNISGREAT';
my $got = join '', @result;
die "@result is not expected" if $got ne $expected;
print "@result - $got\n";

我添加的唯一“单行”事物是while循环中的push map grep m//g。请注意,Perl 5.10添加了“已定义或”运算符 - // - 允许您编写:

push @result, map $proteins{$_} // (), m/(...)/g;

啊好吧,open do local $/文件slurp成语很方便将小文件插入内存。希望你觉得它有点鼓舞人心。 : - )

答案 4 :(得分:0)

如果将蛋白质数据写入另一个文件,空格分隔且没有换行符。因此,您可以通过一次读取文件来导入数据。

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

open(INPUT, "<mydata.txt");
open(DATA, "<proteins.txt");
my %proteins = split(" ",<DATA>);

while (<INPUT>) {
    tr/GCTA/CGAU/;
    while(/(\w{3})/gi) {print $proteins{$1} if (exists($proteins{$1}))};
}
close(INPUT);
close(DATA);

您可以删除代码行“ tr / a,c,g,t / A,C,G,T / ”因为匹配运算符有案例选项不敏感( i 选项)。原始的 foreach 循环可以像上面的代码一样进行优化。 $ 1 变量这里匹配模式结果在匹配操作的括号内 /(\ w {3})/ gi