web-dev-qa-db-fra.com

Comment puis-je vérifier si un tableau Perl contient une valeur particulière?

J'essaie de trouver un moyen de vérifier l'existence d'une valeur dans un tableau sans parcourir le tableau.

Je lis un fichier pour un paramètre. J'ai une longue liste de paramètres que je ne veux pas traiter. J'ai placé ces paramètres indésirables dans un tableau @badparams.

Je veux lire un nouveau paramètre et s'il n'existe pas dans @badparams, le traiter. S'il existe dans @badparams, passez à la lecture suivante.

221
Mel

Il suffit de transformer le tableau en un hachage:

my %params = map { $_ => 1 } @badparams;

if(exists($params{$someparam})) { ... }

Vous pouvez également ajouter plus de paramètres (uniques) à la liste:

$params{$newparam} = 1;

Et plus tard, obtenez une liste de paramètres (uniques):

@badparams = keys %params;
181
jkramer

Meilleur usage général - Tableaux particulièrement courts (1 000 éléments ou moins) et codeurs qui ne savent pas quelles optimisations conviennent le mieux à leurs besoins.

# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
  print "found it";
}

Il a été mentionné que grep passe par toutes les valeurs même si la première valeur du tableau correspond. C’est vrai, cependant grep reste extrêmement rapide dans la plupart des cas. Si vous parlez de tableaux courts (moins de 1 000 éléments), la plupart des algorithmes seront quand même assez rapides. Si vous parlez de très longs tableaux (1 000 000 d'éléments), grep est assez rapide, que l'élément soit le premier, le deuxième ou le dernier du tableau.

Cas d'optimisation pour les tableaux plus longs:

Si votre tableau est trié, utilisez une "recherche binaire".

Si la le même tableau est recherché à plusieurs reprises plusieurs fois, copiez-le d'abord dans un hachage, puis vérifiez-le. Si la mémoire est un problème, déplacez chaque élément du tableau dans le hachage. Plus efficace en termes de mémoire, mais détruit le tableau d'origine.

Si les mêmes valeurs sont recherchées à plusieurs reprises dans le tableau, construisez paresseusement un cache. (lors de la recherche dans chaque élément, vérifiez d’abord si le résultat de la recherche a été stocké dans un hachage persistant. Si le résultat de la recherche n’est pas trouvé dans le hachage, recherchez le tableau et placez le résultat dans le hachage persistant afin que la prochaine fois trouvez-le dans le hachage et ignorez la recherche).

Remarque: ces optimisations ne seront plus rapides qu'avec les tableaux longs. Ne pas trop optimiser.

209
Aaron T Harris

Vous pouvez utiliser la fonctionnalité smartmatch dans Perl 5.10 comme suit:

Pour la recherche de valeur littérale, le faire ci-dessous fera l'affaire.

if ( "value" ~~ @array ) 

Pour la recherche scalaire, faire ci-dessous fonctionnera comme ci-dessus.

if ($val ~~ @array)

Pour le tableau en ligne faisant ci-dessous, fonctionnera comme ci-dessus.

if ( $var ~~ ['bar', 'value', 'foo'] ) 

Dans Perl 5.18 , smartmatch est signalé comme étant expérimental. Par conséquent, vous devez désactiver les avertissements en activant expérimental pragma en ajoutant ci-dessous à votre script/module:

use experimental 'smartmatch';

Alternativement, si vous voulez éviter l'utilisation de smartmatch - alors comme Aaron l'a dit, utilisez:

if ( grep( /^$value$/, @array ) ) {
  #TODO:
}
117
Bitmap

Cet article de blog discute des meilleures réponses à cette question.

En résumé, si vous pouvez installer des modules CPAN, les solutions les plus lisibles sont les suivantes:

any(@ingredients) eq 'flour';

ou

@ingredients->contains('flour');

Cependant, un langage plus courant est:

any { $_ eq 'flour' } @ingredients

Mais n'utilisez pas la fonction first()! Cela n'exprime pas du tout l'intention de votre code. N'utilisez pas l'opérateur ~~ "Smart Match": il est cassé. Et n'utilisez pas grep() ni la solution avec un hachage: ils parcourent toute la liste.

any() s'arrêtera dès qu'il aura trouvé votre valeur.

Consultez le blog pour plus de détails.

43
mascip

Même si son utilisation est pratique, il semble que la solution de conversion en hachage coûte beaucoup de performances, ce qui était un problème pour moi.

#!/usr/bin/Perl
use Benchmark;
my @list;
for (1..10_000) {
    Push @list, $_;
}

timethese(10000, {
  'grep'    => sub {
            if ( grep(/^5000$/o, @list) ) {
                # code
            }
        },
  'hash'    => sub {
            my %params = map { $_ => 1 } @list;
            if ( exists($params{5000}) ) {
                # code
            }
        },
});

Sortie du test de référence:

Benchmark: timing 10000 iterations of grep, hash...
          grep:  8 wallclock secs ( 7.95 usr +  0.00 sys =  7.95 CPU) @ 1257.86/s (n=10000)
          hash: 50 wallclock secs (49.68 usr +  0.01 sys = 49.69 CPU) @ 201.25/s (n=10000)
11
aksel

le repère de @ eakssjo est cassé - mesure la création de hachages en boucle par rapport à la création de regexes en boucle. Version corrigée (plus j'ai ajouté List::Util::first et List::MoreUtils::any):

use List::Util qw(first);
use List::MoreUtils qw(any);
use Benchmark;

my @list = ( 1..10_000 );
my $hit = 5_000;
my $hit_regex = qr/^$hit$/; # precompute regex
my %params;
$params{$_} = 1 for @list;  # precompute hash
timethese(
    100_000, {
        'any' => sub {
            die unless ( any { $hit_regex } @list );
        },
        'first' => sub {
            die unless ( first { $hit_regex } @list );
        },
        'grep' => sub {
            die unless ( grep { $hit_regex } @list );
        },
        'hash' => sub {
            die unless ( $params{$hit} );
        },
    });

Et résultat (c'est pour 100_000 itérations, dix fois plus que dans la réponse de @ eakssjo):

Benchmark: timing 100000 iterations of any, first, grep, hash...
       any:  0 wallclock secs ( 0.67 usr +  0.00 sys =  0.67 CPU) @ 149253.73/s (n=100000)
     first:  1 wallclock secs ( 0.63 usr +  0.01 sys =  0.64 CPU) @ 156250.00/s (n=100000)
      grep: 42 wallclock secs (41.95 usr +  0.08 sys = 42.03 CPU) @ 2379.25/s (n=100000)
      hash:  0 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU) @ 10000000.00/s (n=100000)
            (warning: too few iterations for a reliable count)
10
Xaerxess

Méthode 1: grep (peut être prudent tant que la valeur est censée être un regex).

Essayez d'éviter d'utiliser grep si vous recherchez des ressources.

if ( grep( /^$value$/, @badparams ) ) {
  print "found";
}

Méthode 2: recherche linéaire

for (@badparams) {
    if ($_ eq $value) {
       print "found";
    }
}

Méthode 3: utilisez un hachage

my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});

Méthode 4: smartmatch

(ajouté dans Perl 5.10, marqué est expérimental dans Perl 5.18).

use experimental 'smartmatch';  # for Perl 5.18
print "found" if ($value ~~ @badparams);

Méthode 5: Utiliser le module de base List::MoreUtils

use List::MoreUtils qw(any uniq);;
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ eq $value} @badparams;
7
Kamal Nayan

@files est un tableau existant

my @new_values =  grep(/^2[\d].[\d][A-za-z]?/,@files);

print join("\n", @new_values);

print "\n";

/^2[\d].[\d][A-za-z]?/ = les valeurs à partir de 2 ici, vous pouvez mettre n'importe quelle expression régulière

3
Rohan

Vous voulez certainement un hash ici. Placez les paramètres incorrects en tant que clés dans le hachage, puis décidez si un paramètre particulier existe dans le hachage.

our %bad_params = map { $_ => 1 } qw(badparam1 badparam2 badparam3)

if ($bad_params{$new_param}) {
  print "That is a bad parameter\n";
}

Si vous êtes vraiment intéressé à le faire avec un tableau, regardez List::Util ou List::MoreUtils

2
David M

Vous pouvez le faire de deux manières. Vous pouvez utiliser le jeter les valeurs dans un hachage pour une table de recherche, comme suggéré par les autres publications. (J'ajouterai juste un autre idiome.)

my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;

Mais si ce sont des données composées principalement de caractères Word et pas trop de méta, vous pouvez les transférer dans une alternance regex:

use English qw<$LIST_SEPARATOR>;

my $regex_str = do { 
    local $LIST_SEPARATOR = '|';
    "(?:@bad_params)";
 };

 # $front_delim and $back_delim being any characters that come before and after. 
 my $regex = qr/$front_delim$regex_str$back_delim/;

Cette solution devrait être adaptée aux types de "mauvaises valeurs" que vous recherchez. Et encore une fois, cela peut être totalement inapproprié pour certains types de chaînes, donc caveat emptor.

0
Axeman