【问题标题】:How to Rewrite of One Line Code (or Less Line Code in command line) of this code in Perl?如何在 Perl 中重写此代码的一行代码(或命令行中的少行代码)?
【发布时间】:2023-03-13 20:38:01
【问题描述】:

我有这样的代码:

#!/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变量写入文件。

【问题讨论】:

  • 不要。可读性很好。
  • 虽然 Perl 肯定有许多有趣的单行代码,但它们并不总是比更长、更冗长的代码更好。您有什么具体的想要改进的吗?
  • 与家庭作业无关,因为代码足以完成家庭作业。只是我想了解如何用更少的行代码改进该代码,因为我对单行编码感兴趣。
  • 另外值得注意的是,在字符类中使用逗号是不必要的,好像我没有弄错你只是在制作相应的逗号逗号。相反,您可能希望使用 [acgt] 和 [ACGT],尽管我相信 TR 的参数充当一种字符类。

标签: perl code-golf


【解决方案1】:

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

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

由于ytr 是同义词,您应该只使用其中一个。我认为try 读起来更好,所以我选择了tr。此外,您对它们的称呼非常不同,但这应该是相同的效果,并且只提及您实际更改的字母。 (所有其他角色都被转换为自己。这使得 更难看到实际发生了什么变化。)

您可能希望删除 open(INPUT,"&lt;dna.txt"); 和相应的 close(INPUT); 行,因为它们会使您的程序更难在 shell 管道或不同的输入文件中使用。但这取决于您,如果输入文件将始终dna.txt 并且永远不会有任何不同,那没关系。

【讨论】:

    【解决方案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 将其中一个映射到另一个编码正确蛋白质的位置。

    还要注意% 运算符之前的括号,这both, 运算符与substr 的参数列表隔离开 修复@987654330 的优先级@ 与 %。如果您曾经在生产代码中使用它,那您就是一个坏人。

    【讨论】:

    • 把蛋白质放入proteins.txt怎么样,是不是让代码变少了?
    • 这是另一个问题——但是,是的,您可以通过将 63 个字符的字符串放入蛋白质.txt(不带引号)来节省一点;然后事实证明,将其转换为数组非常容易,通过替换 substr 调用节省了很多。因为我使用-n 标志来读取文件,所以需要一些逻辑来隐藏数据,但这是一个整体的胜利:perl -nE'/F/?@@=/./g:say+map+@@[(s/GGG/GGC/i,vec($_,0,32)&amp;101058048)%63],/.../g' proteins.txt dna.txt
    • 是的,它通过忽略大小写不同的位(0x20)来进行大小写折叠。另一方面,在 EBCDIC Perl 上它可能会失败。
    • @kamaci,我不认为单行编码有很好的职业,但它确实磨练了你对语言中边缘情况的认识,否则会因为你碰巧调用它们而咬你。这很有趣。 Perl 是这方面最糟糕/最有趣的语言之一。 (我自己的生产代码在strictwarnings 下运行干净,并检查每个系统调用——开始)。
    • @kamaci:我通过运行另一个 Perl 程序将UUU F ... 表更改为字符串,以找出每个三字母 DNA 密码子在 (s/GGG/GGC/i,vec($_,0,32)&amp;101058048)%63 操作下变为 0 到 62 之间的哪个数字,然后将正确的字符放在字符串中的每个位置。 % 字符用于占据不编码蛋白质的位置,否则字符串索引会出错。
    【解决方案3】:
    #!/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 以保存另外两个字符。或者,如果输入始终相同,您可以立即将其分配给$_,而不是从任何地方读取。

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

    我在 cmets 中提到的其他几个版本:

    从文件中读取 %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'
    

    【讨论】:

    • 如何从文件中获取 %p 变量以及如何定义 DATA 变量?
    • @kamaci:DATA 是一个特殊的文件句柄,它指向由__DATA__ 分隔的数据段的开头。 __DATA__ 之后的所有内容都被视为注释,您可以使用 &lt;DATA&gt; 从中读取。但是,如果您想从文件中读取所有内容,可以尝试以下方法:pastebin.com/RHNdZbU5
    • 您的解决方案看起来很完美。再给我学一个。我有一个问题:stackoverflow.com/questions/5317461/…,其中一个答案是:perl -0777ne "print+(@@=/count/g)+0" awesome.pl 我们可以这样更改代码吗?
    • @kamaci:我不得不承认我在命令行中没有做太多的 perl 高尔夫,公平地说,我认为这样直接运行的脚本有点太长了(在至少直到有人插话并告诉如何有效地处理多个文件;))。无论如何:perl -e 'open A,"&lt;p.txt";map{map{/(...)/;$p{$1}=chop}/(... .)/g}&lt;A&gt;;open B,"&lt;dna.txt";$_=uc&lt;B&gt;;y/GCTA/CGAU/;map{print if$_=$p{$_}}/(...)/g'
    • 当我从命令行运行该代码时,当我在它们的目录中时,它说类似文件未找到?
    【解决方案4】:

    大部分内容已经被指出,尤其是可读性很重要。我不会试图比下面的更多地减少程序。

    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 成语对于将小文件 slurp 到内存中很方便。希望你觉得它有点鼓舞人心。 :-)

    【讨论】:

      【解决方案5】:

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

      #!/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

      【讨论】:

        猜你喜欢
        • 2019-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-09
        • 1970-01-01
        • 2018-03-28
        • 1970-01-01
        相关资源
        最近更新 更多