Come riscrivere il codice su una riga (o meno codice sulla riga di comando) di questo codice in Perl?
Domanda
Ho un codice del genere:
#!/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);
Questo codice è correlato alla risposta alla mia altra domanda: DNA in RNA e ottenere proteine con Perl
L'output del programma è:
SIMQNISGREAT
Come posso riscrivere quel codice con Perl, verrà eseguito sulla riga di comando e verrà riscritto con meno codice (se possibile codice a riga)?
PS1: dna.txt è così:
TCATAATACGTTTTGTATTCGCCAGCGCTTCGGTGT
PS2: Se il codice sarà di meno righe è consentito scrivere il file my %proteins
variabile in un file.
Soluzione
Qualcuno (@kamaci) ha chiamato il mio nome in un altro thread.Questo è il massimo che riesco a trovare mantenendo la tabella delle proteine sulla riga di comando:
perl -nE'say+map+substr("FYVDINLHL%VEMKLQL%VEIKLQFYVDINLHCSGASTRPWSGARTRP%SGARTRPCSGASTR",(s/GGG/GGC/i,vec($_,0,32)&101058048)%63,1),/.../g' dna.txt
(Citazione di Shell, per Windows citazione di swap '
E "
caratteri).Questa versione contrassegna i codoni non validi con %
, probabilmente puoi risolverlo aggiungendo =~y/%//d
in un punto appropriato.
Suggerimento:Questo seleziona 6 bit dalla codifica ASCII grezza di una tripla RNA, fornendo 64 codici compresi tra 0 e 101058048;per ottenere un indice di stringa riduco il risultato modulo 63, ma questo crea una doppia mappatura che purtroppo ha dovuto codificare due proteine diverse.IL s/GGG/GGC/i
ne associa uno a un altro che codifica la proteina giusta.
Notare anche le parentesi prima di %
operatore quale Entrambi isolare il ,
operatore dall'elenco degli argomenti di substr
E fissare la precedenza di &
contro %
.Se mai lo usi nel codice di produzione, sei una persona cattiva, cattiva.
Altri suggerimenti
Le uniche modifiche che consiglierei di fare sono la semplificazione del ciclo while
:
while (<INPUT>) {
tr/acgt/ACGT/;
tr/GCTA/CGAU/;
foreach my $protein (/(...)/g) {
if (defined $proteins{$protein}) {
print $proteins{$protein};
}
}
}
Poiché y
e tr
sono sinonimi, dovresti utilizzare solo uno di essi.Penso che tr
legga meglio di y
, quindi ho scelto tr
.Inoltre, le stavi chiamando in modo molto diverso, ma questo dovrebbe essere lo stesso effetto e menziona solo le lettere che effettivamente cambi.(Tutti gli altri personaggi venivano trasposti su se stessi. Ciò rende molto più difficile vedere cosa viene effettivamente cambiato.)
Potresti voler rimuovere il open(INPUT,"<dna.txt");
e le corrispondenti righe close(INPUT);
, poiché rendono molto più difficile usare il tuo programma nelle pipeline della shell o con diversi file di input.Ma dipende da te, se il file di input sarà sempre dna.txt
e mai niente di diverso, va bene.
#!/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
Phew.Il meglio che riesco a trovare, almeno così velocemente.Se sei sicuro che l'input sia sempre in maiuscolo, puoi anche rilasciare il uc
salvando altri due caratteri.Oppure, se l'input è sempre lo stesso, puoi assegnarlo subito a $_
invece di leggerlo da qualsiasi luogo.
Immagino di non aver bisogno di dire che questo codice non deve essere utilizzato in ambienti di produzione o altrove se non per puro divertimento.Quando si esegue la programmazione effettiva, la leggibilità vince quasi sempre sulla compattezza.
Alcune altre versioni che ho menzionato nei commenti:
Lettura di% p e del DNA dai file:
#!/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
Dalla shell con 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'
La maggior parte delle cose è già stata evidenziata, soprattutto che la leggibilità è importante.Non proverei a ridurre il programma più di quanto segue.
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";
L'unica cosa "one-liner" che ho aggiunto è il push map grep m//g
nel ciclo while.Nota che Perl 5.10 aggiunge l'operatore "definito o" - //
- che ti permette di scrivere:
push @result, map $proteins{$_} // (), m/(...)/g;
Ah okay, l'idioma slurp del file open do local $/
è utile per caricare piccoli file in memoria.Spero che lo trovi un po 'stimolante.:-)
Se scrive i dati delle proteine in un altro file, delimitato da spazi e senza interruzione di riga.Quindi, puoi importare i dati leggendo il file una volta.
#!/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);
Puoi rimuovere la riga di codice " tr / a, c, g, t / A, C, G, T / " perché l'operatore di corrispondenza ha l'opzione per il casoinsensibile (opzione i ).E il ciclo foreach originale può essere ottimizzato come il codice sopra.La variabile $1 qui corrisponde al risultato del modello tra parentesi dell'operazione di corrispondenza /(\w{3})/gi