2016-12-11 3 views
4

Я пытаюсь отфильтровать массив терминов, используя другой массив в Perl. У меня Perl 5.18.2 на OS X, хотя поведение такое же, если я use 5.010. Вот моя основная установка:Perl: удаление элементов массива и изменение размера массива

#!/usr/bin/perl 
#use strict; 
my @terms = ('alpha','beta test','gamma','delta quadrant','epsilon', 
      'zeta','eta','theta chi','one iota','kappa'); 
my @filters = ('beta','gamma','epsilon','iota'); 
foreach $filter (@filters) { 
    for my $ind (0 .. $#terms) { 
     if (grep { /$filter/ } $terms[$ind]) { 
      splice @terms,$ind,1; 
     } 
    } 
} 

Это работает, чтобы вытащить строки, которые соответствуют различным условиям поиска, но длина массива не изменяется. Если я выписывать в результате @terms массив, я получаю:

[alpha] 
[delta quadrant] 
[zeta] 
[eta] 
[theta chi] 
[kappa] 
[] 
[] 
[] 
[] 

Как и следовало ожидать от этого, печать scalar(@terms) получает результат 10.

Я хочу получить результирующий массив длиной 6, без четырех пустых элементов в конце. Как получить этот результат? И почему матрица не уменьшается, учитывая, что perldoc page about splice говорит: «Массив растет или сжимается по мере необходимости»?

(Я не очень свободно владею Perl, поэтому, если вы думаете: «Почему бы вам не просто ...?», Это почти наверняка, потому что я не знаю об этом или не понимаю его когда я услышал об этом.)

+1

'grep' работает на массивах и возвращает соответствующие элементы. Может быть, вы имеете в виду '$ terms [$ ind] = ~/$ filter /', чтобы соответствовать одному? – tadman

+0

Да, похоже, что это работает, как и предполагалось, спасибо! Я все еще смущен тем, почему массив не сжимался тем, что я делал раньше. –

+0

Всегда сложно удалить элементы из массива, который вы активно итерируете. Это сдвигает смещение на 1 каждый раз, когда вы соедините что-то. – tadman

ответ

7

Вы всегда можете восстановить массив за вычетом того, чего не хотите. grep действует как фильтр позволяет вам решить, какие элементы вы хотите, и которые вы не:

#!/usr/bin/perl 

use strict; 

my @terms = ('alpha','beta test','gamma','delta quadrant','epsilon', 
      'zeta','eta','theta chi','one iota','kappa'); 
my @filters = ('beta','gamma','epsilon','iota'); 

my %filter_exclusion = map { $_ => 1 } @filters; 

my @filtered = grep { !$filter_exclusion{$_} } @terms; 

print join(',', @filtered) . "\n"; 

Это довольно легко, если у вас есть простая структура, как %filter_exclusion на руку.

Update: Если вы хотите, чтобы произвольно подстрок:

my $filter_exclusion = join '|', map quotemeta, @filters; 

my @filtered = grep { !/$filter_exclusion/ } @terms; 
+0

Это лишь частично работает - он отфильтровывает «гамма» и «эпсилон», но не «бета-тест» или «один йота». Тем не менее, полезно иметь под рукой будущие проекты! –

+0

Добавлена ​​версия, которая проверяет произвольные подстроки. Это снова использует регулярное выражение, но только один тест на запись, а не N тестов. – tadman

+0

Прохладный, спасибо! Это действительно работает. Имейте в виду, я понятия не имею, как и почему это работает. –

0

Чтобы понять, что происходит, вывести содержимое массива на каждом шаге: Когда вы сращивание массив, она сжимается, но ваша петля повторяется над 0 .. $ # членами, поэтому в конце цикла $ ind будет указывать за конец массива. Когда вы используете grep { ... } $array[ $too_large ], Perl должен иметь псевдоним несуществующего элемента в $_ внутри блока grep, поэтому он создает элемент undef в массиве.

#!/usr/bin/perl 
use warnings; 
use strict; 
use feature qw{ say }; 

my @terms = ('alpha', 'beta test', 'gamma', 'delta quadrant', 'epsilon', 
      'zeta', 'eta', 'theta chi', 'one iota', 'kappa'); 
my @filters = qw(beta gamma epsilon iota); 

for my $filter (@filters) { 
    say $filter; 
    for my $ind (0 .. $#terms) { 
     if (grep { do { 
      no warnings 'uninitialized'; 
      /$filter/ 
     } } $terms[$ind] 
     ) { 
      splice @terms, $ind, 1; 
     } 
     say "\t$ind\t", join ' ', map $_ || '-', @terms; 
    } 
} 

Если вы использовали $terms[$ind] =~ /$filter/ вместо grep, вы все равно получите неинициализированным предупреждение, но нет никакой необходимости псевдонима элемента, он не будет создан.

+0

@ikegami: Я не вижу 'gamma' в выходе. Более того, это не «исправление», оно должно только демострировать ПОЧЕМУ и КОГДА создаются конечные элементы, поэтому они все еще существуют. – choroba

+0

@ikegami: Если я напечатаю «@terms» ', я вижу' alpha delta quadrant zeta eta theta chi kappa'. – choroba

+0

Извините, ошибка возникает, если вы начинаете с '@terms = qw (gamma gamma kappa);'. Вторая гамма переходит в '$ terms [0]', которая не пересматривается. – ikegami