Como reescrever o código de uma linha (ou menos código de linha na linha de comando) deste código em Perl?
Pergunta
Eu tenho um código assim:
#!/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);
Este código está relacionado à resposta da minha outra pergunta: DNA para RNAe obtendo proteínas com Perl
O resultado do programa é:
SIMQNISGREAT
Como posso reescrever esse código com Perl, ele será executado na linha de comando e será reescrito com menos código (se possível, um código de linha)?
PS 1: dna.txt é assim:
TCATAATACGTTTTGTATTCGCCAGCGCTTCGGTGT
PS 2: Se o código tiver menos linhas, é aceito escrever a variável my %proteins
em um arquivo.
Solução
Alguém (@kamaci) chamou meu nome em outro tópico. Isso é o melhor que posso fazer enquanto mantenho a tabela de proteínas na linha de comando:
perl -nE'say+map+substr("FYVDINLHL%VEMKLQL%VEIKLQFYVDINLHCSGASTRPWSGARTRP%SGARTRPCSGASTR",(s/GGG/GGC/i,vec($_,0,32)&101058048)%63,1),/.../g' dna.txt
(citação de shell, para Windows cita '
de troca e caracteres "
). Esta versão marca códons inválidos com %
, você provavelmente pode corrigir isso adicionando =~y/%//d
em um local apropriado.
Dica: Isso seleciona 6 bits da codificação ASCII bruta de um triplo de RNA, fornecendo 64 códigos entre 0 e 101058048; para obter um índice de string, reduzo o módulo de resultado 63, mas isso cria um mapeamento duplo que, infelizmente, teve que codificar duas proteínas diferentes. O s/GGG/GGC/i
mapeia um deles para outro que codifica a proteína certa.
Observe também os parênteses antes do operador %
, que ambos isolam o operador ,
da lista de argumentos de substr
e corrigem a precedência de &
vs %
. Se você já usou isso no código de produção, você é uma pessoa má.
Outras dicas
As únicas alterações que eu recomendaria fazer são simplificar seu loop while
:
while (<INPUT>) {
tr/acgt/ACGT/;
tr/GCTA/CGAU/;
foreach my $protein (/(...)/g) {
if (defined $proteins{$protein}) {
print $proteins{$protein};
}
}
}
Como y
e tr
são sinônimos, você deve usar apenas um deles.Acho que tr
lê melhor do que y
, então escolhi tr
.Além disso, você as estava chamando de maneira muito diferente, mas este deve ser o mesmo efeito e apenas menciona as letras que você realmente mudou.(Todos os outros personagens estavam sendo transpostos para si mesmos. Isso torna muito mais difícil ver o que realmente está sendo alterado.)
Você pode querer remover o open(INPUT,"<dna.txt");
e as linhas close(INPUT);
correspondentes, pois eles tornam muito mais difícil usar seu programa em pipelines de shell ou com diferentes arquivos de entrada.Mas isso é com você, se o arquivo de entrada será sempre dna.txt
e nunca algo diferente, está tudo bem.
#!/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
Ufa.É o melhor que consigo pensar, pelo menos assim rapidamente.Se você tiver certeza de que a entrada já está sempre em maiúsculas, você também pode descartar o uc
salvando outros dois caracteres.Ou se a entrada for sempre a mesma, você pode atribuí-la a $_
imediatamente, em vez de lê-la de qualquer lugar.
Acho que não preciso dizer que este código não deve ser usado em ambientes de produção ou em qualquer outro lugar que não seja pura diversão.Ao fazer a programação real, a legibilidade quase sempre vence a compactação.
Algumas outras versões que mencionei nos comentários:
Lendo% pe o DNA dos arquivos:
#!/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
Do shell com perl -e
:
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'
Muitas coisas já foram apontadas, especialmente a legibilidade é importante.Eu não tentaria reduzir o programa mais do que o seguinte.
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";
A única coisa de "uma linha" que adicionei é o push map grep m//g
no loop while.Observe que o Perl 5.10 adiciona o operador "definido ou" - //
- que permite que você escreva:
push @result, map $proteins{$_} // (), m/(...)/g;
Certo, o idioma slurp do arquivo open do local $/
é útil para inserir pequenos arquivos na memória.Espero que você ache isso um pouco inspirador.:-)
Se gravar dados de proteínas em outro arquivo, delimitado por espaço e sem quebra de linha.Portanto, você pode importar dados lendo o arquivo uma vez.
#!/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);
Você pode remover a linha de código " tr / a, c, g, t / A, C, G, T / " porque o operador de correspondência tem opção para maiúsculas e minúsculasinsensível (opção i ).E o loop foreach original pode ser otimizado como o código acima.A variável $1 aqui é o resultado do padrão combinado entre parênteses da operação de combinação /(\w{3})/gi