it-swarm.com.de

Wie kann ich überprüfen, ob ein Perl-Array einen bestimmten Wert enthält?

Ich versuche, eine Möglichkeit zu finden, um die Existenz eines Werts in einem Array zu überprüfen, ohne das Array durchlaufen zu müssen.

Ich lese eine Datei für einen Parameter. Ich habe eine lange Liste von Parametern, mit denen ich mich nicht befassen möchte. Ich habe diese unerwünschten Parameter in ein Array gestellt @badparams.

Ich möchte einen neuen Parameter lesen und wenn er in @badparams Nicht existiert, diesen verarbeiten. Wenn es in @badparams Existiert, fahren Sie mit dem nächsten Lesevorgang fort.

221
Mel

Verwandle das Array einfach in einen Hash:

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

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

Sie können der Liste auch weitere (eindeutige) Parameter hinzufügen:

$params{$newparam} = 1;

Und später eine Liste der (eindeutigen) Parameter zurückbekommen:

@badparams = keys %params;
181
jkramer

Bester allgemeiner Zweck - Besonders kurze Arrays (1000 Elemente oder weniger) und Codierer, die sich nicht sicher sind, welche Optimierungen ihren Anforderungen am besten entsprechen.

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

Es wurde erwähnt, dass grep alle Werte durchläuft, auch wenn der erste Wert im Array übereinstimmt. Dies ist jedoch wahr grep ist in den meisten Fällen immer noch extrem schnell. Wenn es sich um kurze Arrays (weniger als 1000 Elemente) handelt, sind die meisten Algorithmen sowieso ziemlich schnell. Wenn es sich um sehr lange Arrays (1.000.000 Elemente) handelt, ist grep akzeptabel schnell, unabhängig davon, ob das Element das erste oder das mittlere oder das letzte Element im Array ist.

Optimierungsfälle für längere Arrays:

Wenn Ihr Array sortiert istVerwenden Sie eine "binäre Suche".

Wenn das das gleiche Array wird wiederholt durchsucht oft kopiere es zuerst in einen Hash und überprüfe dann den Hash. Wenn der Speicher ein Problem darstellt, verschieben Sie jedes Element aus dem Array in den Hash. Effizienter Speicher, zerstört aber das ursprüngliche Array.

Wenn gleiche Werte werden wiederholt gesucht Erstellen Sie innerhalb des Arrays einen Cache. (Überprüfen Sie beim Durchsuchen jedes Elements zunächst, ob das Suchergebnis in einem dauerhaften Hash gespeichert wurde. Wenn das Suchergebnis nicht im Hash gefunden wird, durchsuchen Sie das Array und geben Sie das Ergebnis in den dauerhaften Hash ein, damit es beim nächsten Mal angezeigt wird finde es im Hash und überspringe die Suche).

Hinweis: Diese Optimierungen sind nur bei langen Arrays schneller. Überoptimieren Sie nicht.

209
Aaron T Harris

Sie können die Smartmatch-Funktion in Perl 5.10 wie folgt verwenden:

Für die Suche nach Literalwerten reicht die folgende Vorgehensweise aus.

if ( "value" ~~ @array ) 

Bei der Skalarsuche funktioniert das Ausführen von "Unten" wie oben.

if ($val ~~ @array)

Bei Inline-Arrays funktioniert dies wie oben beschrieben.

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

In Perl 5.18 ist Smartmatch als experimentell markiert, daher müssen Sie die Warnungen ausschalten, indem Sie experimentell Pragma einschalten, indem Sie unten zu hinzufügen Ihr Skript/Modul:

use experimental 'smartmatch';

Alternativ, wenn Sie die Verwendung von Smartmatch vermeiden möchten - dann verwenden Sie, wie Aaron sagte:

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

Dieser Blog-Beitrag diskutiert die besten Antworten auf diese Frage.

Kurz zusammengefasst, wenn Sie CPAN-Module installieren können, sind die am besten lesbaren Lösungen:

any(@ingredients) eq 'flour';

oder

@ingredients->contains('flour');

Eine häufigere Redewendung ist jedoch:

any { $_ eq 'flour' } @ingredients

Aber bitte benutze nicht die first() Funktion! Es drückt überhaupt nicht die Absicht Ihres Codes aus. Verwenden Sie nicht den "Smart Match" -Operator ~~: Er ist fehlerhaft. Und verwenden Sie weder grep() noch die Lösung mit einem Hash: Sie durchlaufen die gesamte Liste.

any() stoppt, sobald es Ihren Wert findet.

Weitere Informationen finden Sie im Blogbeitrag.

43
mascip

Obwohl es bequem zu bedienen ist, scheint es, dass die Konvertierung in eine Hash-Lösung ziemlich viel Leistung kostet, was für mich ein Problem war.

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

Ausgabe des Benchmarktests:

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

@ eakssjos Benchmark ist defekt - Misst das Erzeugen von Hashes in einer Schleife im Vergleich zum Erzeugen von Regexes in einer Schleife. Feste Version (außerdem habe ich List::Util::first Und List::MoreUtils::any Hinzugefügt):

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} );
        },
    });

Und das Ergebnis (das sind 100_000 Iterationen, zehnmal mehr als in @ eakssjos Antwort):

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

Methode 1: grep (kann vorsichtig sein, wenn erwartet wird, dass der Wert ein regulärer Ausdruck ist).

Vermeiden Sie die Verwendung von grep, wenn Sie Ressourcen betrachten.

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

Methode 2: Lineare Suche

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

Methode 3: Verwenden Sie einen Hash

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

Methode 4: Smartmatch

(hinzugefügt in Perl 5.10, markiert ist experimentell in Perl 5.18).

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

Methode 5: verwenden Sie Kernmodul 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 ist ein vorhandenes Array

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

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

print "\n";

/^2[\d‹.[\d‹A-za-z‹?/ = vaues ab 2 hier können sie jeden regulären ausdruck setzen

3
Rohan

Sie wollen auf jeden Fall einen Hash hier. Platzieren Sie die fehlerhaften Parameter als Schlüssel im Hash und entscheiden Sie dann, ob ein bestimmter Parameter im Hash vorhanden ist.

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

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

Wenn Sie wirklich daran interessiert sind, dies mit einem Array zu tun, schauen Sie sich List::Util oder List::MoreUtils

2
David M

Es gibt zwei Möglichkeiten, dies zu tun. Sie können die Werte in einen Hash für eine Nachschlagetabelle werfen, wie von den anderen Posts vorgeschlagen. (Ich werde nur eine weitere Redewendung hinzufügen.)

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

Wenn es sich jedoch um Daten handelt, die zumeist aus Word-Zeichen und nicht zu vielen Meta-Zeichen bestehen, können Sie sie in eine Regex-Abwechslung kopieren:

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/;

Diese Lösung müsste auf die Arten von "schlechten Werten" abgestimmt werden, nach denen Sie suchen. Und wieder, es könnte für bestimmte Arten von Saiten völlig ungeeignet sein, also Vorbehalt-Emptor.

0
Axeman