Perl: сопоставление элемента массива с последующим копированием ПРЕДЫДУЩЕГО (5 индексов назад) элемента массива в новый массив

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

Я пробовал что-то вроде этого,

$fastafile = 'HpHcTEST.txt';
open(FASTAFILE, $fastafile);
@seq = <FASTAFILE>;
my $fastaid;
foreach (@seq) {
    if ($_ =~ /\*\*\*\*\* No hits found \*\*\*\*\*/){
        $fastaid .= $_[-5];
    }
}

print "here are the IDs\n";
print $fastaid;

с кучей вариантов [-5], но ни один из них не сработал. Кажется, я не могу найти никакой документации о том, как сделать обратную ссылку и получить предыдущий элемент, если встречается совпадение. Кто-нибудь знает, как кодировать для этого?

Большое спасибо за уделенное время.

Андрей


person amrezans    schedule 24.04.2012    source источник


Ответы (3)


Быстрое решение

Один из способов сделать это — пройтись по @seq с индексом.

my @fastaid;

for (my $i = 0; $i < @seq; ++$i) {
    if ($seq[$i] =~ /\*\*\*\*\* No hits found \*\*\*\*\*/){
        push @fastaid, $seq[$i - 5] if $i >= 5;
    }
}

Обратите внимание на переход от скаляра к массиву с именем @fastaid, который вы можете распечатать, используя

print "Here are the IDs:\n";
print "  - $_\n" for @fastaid;

или даже

print "Here are the IDs:\n",
      map "  - $_\n", @fastaid;

Добавление лака

Как отмечает brian d foy в комментарии ниже, код мог бы быть более элегантным и выражать намерение более прямо.

my $id_offset = 5;
my @fastaid;

for ($id_offset .. $#seq) {
    if ($seq[$_] =~ /\*\*\*\*\* No hits found \*\*\*\*\*/){
        push @fastaid, $seq[$_ - $id_offset];
    }
}

Как задокументировано в разделе "Скалярные значения" perldata, $#seq – это индекс или последний элемент в @seq. ..оператор диапазона правильно обрабатывает случай, когда @seq меньше $id_offset элементов в длина.

Явный оператор regex-bind все еще немного неидеален. Вы могли бы пойти с

my $id_offset = 5;
my @fastaid;

for my $i ($id_offset .. $#seq) {
  for ($seq[$i]) {
    push @fastaid, $seq[$i - $id_offset]
      if /\*\*\*\*\* No hits found \*\*\*\*\*/;
  }
}

или если у вас есть хотя бы версия 5.10

use feature 'switch';

# ...

my $id_offset = 5;
my @fastaid;

for my $i ($id_offset .. $#seq) {
  given ($seq[$i]) {
    when (/\*\*\*\*\* No hits found \*\*\*\*\*/) {
      push @fastaid, $seq[$i - $id_offset];
    }
  }
}

Историческая справка

Когда-то ходили разговоры о перепрофилировании $# для отслеживания индекса обхода массива, чтобы можно было написать

for (@fastaid) {
    if (/\*\*\*\*\* No hits found \*\*\*\*\*/) {
        push @fastaid, $seq[$# - 5] if $# >= 5;
    }
}

но это так и не материализовалось.

person Greg Bacon    schedule 24.04.2012
comment
Вам, вероятно, следует начать с индекса 5 вместо 0, если вы собираетесь вернуть пять элементов. В противном случае совпадение в позициях с 0 по 4 приведет к захвату элементов, потенциально опережающих вашу позицию. - person brian d foy; 25.04.2012
comment
@briandfoy Я согласен, что это немного неэлегантно, но разве модификатор ... if $i >= 5 этого не улавливает? - person Greg Bacon; 25.04.2012
comment
Ах да, хотя я сначала прочитал ваш $# комментарий. Хотя, конечно, неэлегантно. :) - person brian d foy; 25.04.2012

Вы можете перебирать индексы и индексы, чтобы получить элементы массива:

for (5..$#seq) {
    $fastaid .= $seq[$_-5] if $seq[$_] =~ /your_regex/;
}

В Perl 5.12 или выше вы также можете использовать each:

while (my ($index, $value) = each @seq) {
    next if $index < 5;
    $fastaid .= $seq[$index-5] if $value =~ /your_regex/;
}
person Eugene Yarmash    schedule 24.04.2012

Используйте цикл for вместо foreach,

for ($index=0; $index < $#seq + 1; $index++) {
    if ($seq[$index] =~ /\*\*\*\*\* No hits found \*\*\*\*\*/){
        $fastaid .= $seq[$index-5];
    }
}
person dpp    schedule 24.04.2012
comment
В Perl for и foreach — синонимы, полностью взаимозаменяемые. Более идиоматичное сравнение — $index <= $#seq. - person Greg Bacon; 25.04.2012
comment
Спасибо всем за быстрые и полезные отзывы! - person amrezans; 25.04.2012
comment
В Perl можно написать for my $index (0 .. $#seq) { - person Sinan Ünür; 25.04.2012