Perl функция для нарезки строки с начала, конца и шага - PullRequest
1 голос
/ 25 марта 2020

Я хочу нарезать строку, используя начало, конец и шаг. Давайте предположим, что начальная координата равна 1, конечная координата равна 22, шаг равен 3. При вырезании строки следует выбирать только элементы, которые находятся в координатах 1, 4, 7, 10, 13, 16, 19, 22. Я написал функцию get_subseq это делает это Есть ли более короткий способ сделать это в Perl?

sub get_subseq {
    my ( $seq, $start, $end, $step ) = @_;
    my $index = $start;
    while ( $index <= $end ) {
        print substr $seq, $index, 1;
        $index += $step;
    }
}

my $sequence = 'AGGGTAGAGTGAGAAGCACCAGCAGGCAGTAACAGC';

# The result should be GTAGACCC
get_subseq( $sequence, 1, 22, 3 );

Ответы [ 3 ]

7 голосов
/ 25 марта 2020

Один способ: создать список индексов и затем использовать map для извлечения соответствующих символов.

Свернутое в один оператор

use warnings;
use strict;
use feature 'say';

my $seq = q(AGGGTAGAGTGAGAAGCACCAGCAGGCAGTAACAGC); 

my ($beg, $end, $step) = (1, 22, 3); 

my @subseq = 
    map { substr $seq, $_, 1 } 
    grep { ($_-$beg) % $step == 0 } 
    $beg..$end; 

say "@subseq";

Это можно свернуть в одну итерацию по $beg..$end range

my @subseq = 
    map { ($_-$beg) % $step == 0 ? substr($seq, $_, 1) : () }
    $beg..$end; 

Если результатом должна быть строка join список по '' (пустая строка).

И, конечно, есть библиотеки, которые могут произвести Диапазон с шагом. List :: Gen имеет такую ​​функцию range, хотя он также имеет целый ряд интересных алгоритмов.

use List::Gen qw(range);

my @ss = map { substr $seq, $_, 1 } @{ range $beg, $end, $step };

say "@ss";

Его range возвращает действительно генератор, который приходит с интересными свойствами. Разыменование создает список значений. См. Документацию.

Хотя они возвращают результат в одном утверждении и, таким образом, «короче», мне нравится ваш собственный ответ на вопрос, который кристально ясен и может быть более эффективным во многих обстоятельствах.

Это может быть упрощено немного (редким!) Использованием C стиля for l oop

for (my $i = $beg; $i <= $end; $i += $step) { print substr $seq, $i, 1 }

Другой способ заключается в разбить вашу строку на список ее символов, а затем извлечь из этого списка элементы в нужных позициях

my @subseq = (split //, $seq)[ @indices ];

, где вы можете использовать любой метод для получения @indices (который не обязательно должен быть массивом, но может быть список, сгенерированный прямо здесь, любым способом, использованным выше, например). Какой из этих двух подходов более эффективен, полностью зависит от деталей - длины последовательности, длины диапазона индекса до выборки, их отношения, размера шага.

5 голосов
/ 25 марта 2020

Никто не любит использовать регулярные выражения?

join'',substr($seq,$start,$end-$start+1)=~/(?=(.)).{0,$step}/gs

Это демонстрирует использование соответствия регулярным выражениям для такого рода вещей, причем иногда это может быть более эффективным, чем циклы или разбиение и разрезание. Более забавным, но не эффективным способом было бы отказаться от substr:

join '',$seq=~/(?<=.{$start})(?<!..{$end})(?=(.)).{0,$step}/gs
2 голосов
/ 25 марта 2020

Решение можно найти, взяв символы в интересующей позиции.

Шаг # 1: сгенерировать массив индексов

Шаг # 2: разбить $ seq на массив

Шаг # 3: выбрать символы в позиции индекса массива $ seq

Шаг # 4: объединить символы результата

Шаг # 5: результат вывода

Это намного быстрее, чем использование substr несколько раз

use strict;
use warnings;
use feature 'say';

my $seq = 'AGGGTAGAGTGAGAAGCACCAGCAGGCAGTAACAGC';
my($pos,$end,$inc) = (1,22,3);
my @index;

for(;$pos<=$end;$pos+=$inc) { push @index, $pos; }

say join('',(split('',$seq))[@index]);

Вывод

GTAGACCC
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...