На прошлой неделе я решил попробовать Perl6 и начал переориентировать одну из моих программ. Я должен сказать, что Perl6 - это простое программирование объектов, что очень мучительно для меня в Perl5.
Моя программа должна читать и хранить большие файлы, такие как целые геномы (до 3 ГБ и более, см. Пример 1 ниже) или данные таблицы.
Первая версия кода была сделана в Perl5 путем повторения строки за строкой ("genome.fa".IO.lines). Это было очень медленно и небезопасно для правильного времени выполнения.
my class fasta {
has Str $.file is required;
has %!seq;
submethod TWEAK() {
my $id;
my $s;
for $!file.IO.lines -> $line {
if $line ~~ /^\>/ {
say $id;
if $id.defined {
%!seq{$id} = sequence.new(id => $id, seq => $s);
}
my $l = $line;
$l ~~ s:g/^\>//;
$id = $l;
$s = "";
}
else {
$s ~= $line;
}
}
%!seq{$id} = sequence.new(id => $id, seq => $s);
}
}
sub MAIN()
{
my $f = fasta.new(file => "genome.fa");
}
Поэтому после небольшого количества RTFM я изменил для slurp в файле, раскол на \n, который я проанализировал с помощью цикла for. Таким образом мне удалось загрузить данные за 2 мин. Гораздо лучше, но недостаточно. Обманывая, я имею в виду, удалив максимум \n (пример 2), я уменьшил время выполнения до 30 секунд. Довольно хорошо, но не полностью удовлетворенный, этот формат fasta не самый используемый.
my class fasta {
has Str $.file is required;
has %!seq;
submethod TWEAK() {
my $id;
my $s;
say "Slurping ...";
my $f = $!file.IO.slurp;
say "Spliting file ...";
my @lines = $f.split(/\n/);
say "Parsing lines ...";
for @lines -> $line {
if $line !~~ /^\>/ {
$s ~= $line;
}
else {
say $id;
if $id.defined {
%!seq{$id} = seq.new(id => $id, seq => $s);
}
$id = $line;
$id ~~ s:g/^\>//;
$s = "";
}
}
%!seq{$id} = seq.new(id => $id, seq => $s);
}
}
sub MAIN()
{
my $f = fasta.new(file => "genome.fa");
}
Так снова RTFM, и я обнаружил магию грамматики. Так что новая версия и время выполнения 45 секунд, независимо от формата fasta. Не самый быстрый способ, но более элегантный и стабильный.
my grammar fastaGrammar {
token TOP { <fasta>+ }
token fasta {<.ws><header><seq> }
token header { <sup><id>\n }
token sup { '>' }
token id { <[\d\w]>+ }
token seq { [<[ACGTNacgtn]>+\n]+ }
}
my class fastaActions {
method TOP ($/){
my @seqArray;
for $<fasta> -> $f {
@seqArray.push: seq.new(id => $f.<header><id>.made, seq => $f<seq>.made);
}
make @seqArray;
}
method fasta ($/) { make ~$/; }
method id ($/) { make ~$/; }
method seq ($/) { make $/.subst("\n", "", :g); }
}
my class fasta {
has Str $.file is required;
has %seq;
submethod TWEAK() {
say "=> Slurping ...";
my $f = $!file.IO.slurp;
say "=> Grammaring ...";
my @seqArray = fastaGrammar.parse($f, actions => fastaActions).made;
say "=> Storing data ...";
for @seqArray -> $s {
%!seq{$s.id} = $s;
}
}
}
sub MAIN()
{
my $f = fasta.new(file => "genome.fa");
}
Я думаю, что я нашел хорошее решение для обработки таких больших файлов, но все же результаты по-прежнему относятся к Perl5.
Будучи новичком в Perl6, мне было бы интересно узнать, есть ли лучшие способы борьбы с большими данными или если есть какое-то ограничение из-за реализации Perl6?
Будучи новичком в Perl6, я задал бы два вопроса:
- Существуют ли другие механизмы Perl6, которые я еще не знаю или еще не задокументирован, для хранения огромных данных из файла (например, моих геномов)?
- Достиг ли максимальная производительность для текущей версии Perl6?
Спасибо за прочтение !
Фаста Пример 1:
>2L
CGACAATGCACGACAGAGGAAGCAGAACAGATATTTAGATTGCCTCTCATTTTCTCTCCCATATTATAGGGAGAAATATG
ATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCTCTTTGATTTTTTGGCAACCCAAAATGGTGGCGGATGAACGAGAT
...
>3R
CGACAATGCACGACAGAGGAAGCAGAACAGATATTTAGATTGCCTCTCATTTTCTCTCCCATATTATAGGGAGAAATATG
ATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCTCTTTGATTTTTTGGCAACCCAAAATGGTGGCGGATGAACGAGAT
...
Пример Fasta 2:
>2L
GACAATGCACGACAGAGGAAGCAGAACAGATATTTAGATTGCCTCTCAT...
>3R
TAGGGAGAAATATGATCGCGTATGCGAGAGTAGTGCCAACATATTGTGCT...
EDIT Я применил советы @Christoph и @timotimo и проверил код:
my class fasta {
has Str $.file is required;
has %!seq;
submethod TWEAK() {
say "=> Slurping / Parsing / Storing ...";
%!seq = slurp($!file, :enc<latin1>).split('>').skip(1).map: {
.head => seq.new(id => .head, seq => .skip(1).join) given .split("\n").cache;
}
}
}
sub MAIN()
{
my $f = fasta.new(file => "genome.fa");
}
Программа закончилась в 2,7 секунды, что так здорово! Я также пробовал этот код на геноме пшеницы (10 Гб). Это закончилось в 35.2s. Perl6 не так медленно!
Большое спасибо за помощь!