2016-12-11 5 views
4

J'essaie de filtrer un tableau de termes en utilisant un autre tableau en Perl. J'ai Perl 5.18.2 sur OS X, mais le comportement est le même si je use 5.010. Voici ma configuration de base:Perl: Suppression d'éléments de tableau et redimensionnement du tableau

#!/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; 
     } 
    } 
} 

Cela fonctionne pour tirer les lignes qui correspondent aux différents termes de recherche, mais la longueur du tableau ne change pas. Si j'écris sur le tableau @terms résultant, je reçois:

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

Comme vous pouvez attendre de cela, l'impression scalar(@terms) obtient un résultat de 10.

Ce que je veux est un tableau résultant de longueur 6, sans les quatre éléments vides à la fin. Comment puis-je obtenir ce résultat? Et pourquoi le tableau ne rétrécit-il pas, étant donné que le perldoc page about splice dit: «Le tableau croît ou se rétrécit si nécessaire.»?

(Je ne parle pas couramment Perl, donc si vous pensez "Pourquoi ne pas juste ...?", C'est presque certainement parce que je ne le sais pas ou je ne l'ai pas compris quand j'en ai entendu parler.)

+1

'grep' fonctionne sur les tableaux et renvoie les éléments correspondants. Peut-être que vous voulez dire '$ terms [$ ind] = ~/$ filter /' pour correspondre à un seul? – tadman

+0

Oui, ça ressemble à ça, merci! Je ne comprends toujours pas pourquoi le tableau n'a pas rétréci avec ce que je faisais avant. –

+0

Il est toujours difficile de supprimer des éléments d'un tableau que vous itérez activement. Cela décale le décalage de 1 chaque fois que vous fusionnez quelque chose. – tadman

Répondre

7

Vous pouvez toujours régénérer le tableau moins les choses que vous ne voulez pas. grep agit comme un filtre vous permettant de décider quels éléments vous voulez et que vous ne faites:

#!/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"; 

Il est assez facile si vous avez une structure simple comme %filter_exclusion à portée de main.

Mise à jour: Si vous souhaitez autoriser correspond substring arbitraire:

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

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

Celui-ci ne fonctionne que partiellement - il filtre 'gamma' et' epsilon', mais pas 'beta test' ou' one iota'. Utile pour avoir de la main pour de futurs projets, cependant! –

+0

Ajout d'une version qui teste des sous-chaînes arbitraires. Celui-ci utilise à nouveau une expression régulière, mais un seul test par entrée, et non des tests N. – tadman

+0

Cool, merci! Cela fonctionne vraiment. Rappelez-vous, je n'ai aucune idée de comment ou pourquoi cela fonctionne. –

0

Pour voir ce qui se passe, imprimer le contenu du tableau à chaque étape: Lorsque vous Splice le tableau, il se rétrécit, mais votre boucle itère sur 0 .. $ # termes, donc à la fin de la boucle, $ ind pointera derrière la fin du tableau. Lorsque vous utilisez grep { ... } $array[ $too_large ], Perl doit aliaser l'élément inexistant à $_ à l'intérieur du bloc grep, de sorte qu'il crée un élément undef dans le tableau.

#!/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; 
    } 
} 

Si vous avez utilisé $terms[$ind] =~ /$filter/ au lieu de grep, vous souhaitez toujours obtenir des avertissements non initialisées, mais comme il n'y a pas besoin d'un alias l'élément, il ne sera pas créé.

+0

@ikegami: Je ne vois pas 'gamma' dans la sortie. De plus, il ne s'agit pas d'une "solution", elle devrait seulement démontrer pourquoi et quand les éléments finaux sont créés - par conséquent, ils sont toujours là. – choroba

+0

@ikegami: Si j'imprime "@terms" ', je vois' alpha delta quadrant zeta eta theta chi kappa'. – choroba

+0

Oh désolé, le bug se produit si vous commencez par '@terms = qw (gamma gamma kappa);'. Le deuxième gamma est déplacé dans '$ terms [0]', ce qui n'est pas revisité. – ikegami