82 votes

Quelle est la meilleure façon de supprimer une valeur d'un tableau en Perl ?

Le tableau contient beaucoup de données et je dois supprimer deux éléments.

Voici l'extrait de code que j'utilise,

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;

4 votes

Cela supprime trois éléments.

0 votes

J'avais besoin d'enlever tous les éléments qui ne sont pas des fichiers de la liste des répertoires et " array = grep { -f $_ } array " a fonctionné comme un charme pour moi :)

87voto

SquareCog Points 12947

Utilisez splice si vous connaissez déjà l'index de l'élément que vous voulez supprimer.

Grep fonctionne si vous effectuez une recherche.

Si vous devez en faire beaucoup, vous obtiendrez de bien meilleures performances en conservant votre tableau dans un ordre trié, puisque vous pourrez alors effectuer une recherche binaire pour trouver l'index nécessaire.

Si cela a un sens dans votre contexte, vous pouvez envisager d'utiliser une "valeur magique" pour les enregistrements supprimés, plutôt que de les supprimer, afin d'économiser sur le mouvement des données - définir les éléments supprimés à undef, par exemple. Naturellement, cela pose ses propres problèmes (si vous avez besoin de connaître le nombre d'éléments "vivants", vous devez en assurer le suivi séparément, etc.), mais cela peut en valoir la peine selon votre application.

Modifier En fait, maintenant que j'y regarde à deux fois, n'utilisez pas le code grep ci-dessus. Il serait plus efficace de trouver l'index de l'élément que vous voulez supprimer, puis d'utiliser splice pour le supprimer (le code que vous avez accumule tous les résultats non correspondants ).

my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);

Cela supprimera la première occurrence. La suppression de toutes les occurrences est très similaire, sauf que vous voudrez obtenir tous les index en une seule passe :

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

Le reste est laissé comme un exercice pour le lecteur - rappelez-vous que le tableau change au fur et à mesure que vous le coupez !

Edit2 John Siracusa m'a fait remarquer à juste titre que j'avais un bug dans mon exemple corrigé, désolé.

14 votes

Si la chaîne de caractères n'est pas trouvée, la boucle sera bloquée, alors faites my $index = 0 ; my $count = scalar @arr ; $index++ jusqu'à ce que $arr[$index] eq 'foo' ou $index==$count ; splice(@arr, $index, 1) ;

1 votes

O my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); } - pour le premier match

13voto

spoulson Points 13391

épissure supprimera le(s) élément(s) du tableau par index. Utilisez grep, comme dans votre exemple, pour rechercher et supprimer.

0 votes

Merci spoulson. Je ne connais pas les indices que je dois supprimer et j'ai donc dû recourir à grep.

8voto

tvanfosson Points 268301

Est-ce quelque chose que vous allez faire souvent ? Si oui, vous devriez envisager une structure de données différente. Grep va rechercher l'ensemble du tableau à chaque fois, ce qui peut s'avérer assez coûteux pour un grand tableau. Si la vitesse est un problème, vous pouvez envisager d'utiliser un hachage à la place.

Dans votre exemple, la clé serait le nombre et la valeur serait le nombre d'éléments de ce nombre.

5voto

dean Points 41

Si vous changez

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

à

my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);

Cela permet d'éviter le problème de renumérotation du tableau en retirant d'abord les éléments de l'arrière du tableau. En plaçant un splice() dans une boucle foreach, on nettoie @arr. Relativement simple et lisible...

foreach $item (@del_indexes) {
   splice (@arr,$item,1);
}

3voto

Axeman Points 24103

Je pense que votre solution est la plus simple et la plus facile à maintenir.

Le reste du post documente la difficulté de transformer les tests sur les éléments en splice les offsets. Ainsi, cela en fait une complet réponse.

Regardez le girations pour disposer d'un algorithme efficace (c'est-à-dire à passage unique) permettant de transformer les tests sur les éléments de la liste en index. Et ce n'est pas du tout intuitif.

sub array_remove ( \@& ) { 
    my ( $arr_ref, $test_block ) = @_;
    my $sp_start  = 0;
    my $sp_len    = 0;
    for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
        local $_ = $arr_ref->[$inx];
        next unless $test_block->( $_ );
        if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
            splice( @$arr_ref, $sp_start, $sp_len );
            $inx    = $inx - $sp_len;
            $sp_len = 0;
        }
        $sp_start = $inx if ++$sp_len == 1;
    }
    splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
    return;
}

2 votes

Un simple "grep" sera beaucoup plus facile à comprendre et plus efficace que cela.

5 votes

Quelqu'un a supprimé mon commentaire que vous n'avez clairement pas lu le texte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X