AKTUALISIEREN:
Die Smartmatch-Funktionsfamilie ist jetzt experimentell
Smart Match, das in Version 5.10.0 hinzugefügt und in Version 5.10.1 erheblich überarbeitet wurde, war ein regelmäßiger Beschwerdepunkt. Obwohl es eine Reihe von Möglichkeiten gibt, wie es nützlich ist, hat es sich sowohl für Benutzer als auch für Implementierer von Perl als problematisch und verwirrend erwiesen. Es gab eine Reihe von Vorschlägen, wie das Problem am besten angegangen werden kann. Es ist klar, dass sich Smartmatch mit ziemlicher Sicherheit in Zukunft ändern oder verschwinden wird. Es wird nicht empfohlen, sich auf das aktuelle Verhalten zu verlassen.
Warnungen werden jetzt ausgegeben, wenn der Parser ~~ sieht, gegeben ist oder wann.
Wenn Sie Perl v5.10 nicht benötigen, können Sie eines der folgenden Beispiele verwenden.
Der Smart Match~~
Operator.
if( $element ~~ @list ){ ... }
if( $element ~~ [ 1, 2, 3 ] ){ ... }
Sie können auch das Konstrukt given
/when
verwenden. Welches nutzt die Smart Match-Funktionalität intern.
given( $element ){
when( @list ){ ... }
}
Sie können eine for
Schleife auch als "Topicalizer" verwenden (dh sie wird festgelegt $_
).
for( @elements ){
when( @list ){ ... }
}
Eine Sache, die in Perl 5.12 herauskommen wird, ist die Fähigkeit, die Post-Fix-Version von zu verwenden when
. Das macht es noch ähnlicher if
und unless
.
given( $element ){
... when @list;
}
Wenn Sie in der Lage sein müssen, auf älteren Perl-Versionen zu laufen, gibt es noch mehrere Optionen.
Sie könnten denken, Sie könnten zuerst mit List :: Util :: davonkommen , aber es gibt einige Randbedingungen, die es problematisch machen.
In diesem Beispiel ist es ziemlich offensichtlich, dass wir erfolgreich gegeneinander antreten wollen 0
. Leider wird dieser Code failure
jedes Mal gedruckt .
use List::Util qw'first';
my $element = 0;
if( first { $element eq $_ } 0..9 ){
print "success\n";
} else {
print "failure\n";
}
Sie könnten den Rückgabewert von first
auf Definiertheit überprüfen , aber das schlägt fehl, wenn wir tatsächlich möchten, dass eine Übereinstimmung undef
mit erfolgreich ist.
Sie können jedoch sicher verwenden grep
.
if( grep { $element eq $_ } 0..9 ){ ... }
Dies ist sicher, da es grep
in einem skalaren Kontext aufgerufen wird. Arrays geben die Anzahl der Elemente zurück, wenn sie im skalaren Kontext aufgerufen werden. Das wird also auch dann weiter funktionieren, wenn wir versuchen, gegen uns anzutreten undef
.
Sie könnten eine umschließende for
Schleife verwenden. Stellen Sie einfach sicher, dass Sie anrufen last
, um die Schleife bei einem erfolgreichen Match zu verlassen. Andernfalls wird Ihr Code möglicherweise mehrmals ausgeführt.
for( @array ){
if( $element eq $_ ){
...
last;
}
}
Sie könnten die for
Schleife in den Zustand der if
Anweisung einfügen ...
if(
do{
my $match = 0;
for( @list ){
if( $element eq $_ ){
$match = 1;
last;
}
}
$match;
}
){
...
}
... aber es könnte klarer sein, die for
Schleife vor die if
Anweisung zu setzen.
my $match = 0;
for( @list ){
if( $_ eq $element ){
$match = 1;
last;
}
}
if( $match ){ ... }
Wenn Sie nur mit Zeichenfolgen übereinstimmen, können Sie auch einen Hash verwenden. Dies kann Ihr Programm beschleunigen, wenn @list
es groß ist und Sie %hash
mehrmals gegeneinander antreten . Vor allem, wenn @array
sich nichts ändert, denn dann muss man nur einmal laden %hash
.
my %hash = map { $_, 1 } @array;
if( $hash{ $element } ){ ... }
Sie können auch Ihre eigene Unterroutine erstellen. Dies ist einer der Fälle, in denen es nützlich ist, Prototypen zu verwenden .
sub in(&@){
local $_;
my $code = shift;
for( @_ ){
if( $code->() ){
return 1;
}
}
return 0;
}
if( in { $element eq $_ } @list ){ ... }
~~
! es ist so wellig und hilfreich! :)#Smart-matching-in-detail
Fragment mehr.if( $element ~~ @list ){ do_task }
~~
ist der "Smart Match Operator" und bietet mehr als nur die Erkennung von Listenmitgliedschaften.quelle
#Smart-matching-in-detail
Fragment mehr.Wenn Sie dies mehrmals planen, können Sie den Speicherplatz gegen die Suchzeit austauschen:
#!/usr/bin/perl use strict; use warnings; my @array = qw( one ten twenty one ); my %lookup = map { $_ => undef } @array; for my $element ( qw( one two three ) ) { if ( exists $lookup{ $element }) { print "$element\n"; } }
Angenommen, die Häufigkeit, mit der das Element angezeigt wird,
@array
ist nicht wichtig und der Inhalt von@array
ist ein einfacher Skalar.quelle
List :: Util :: first
$foo = first { ($_ && $_ eq "value" } @list; # first defined value in @list
Oder für handrollende Typen:
my $is_in_list = 0; foreach my $elem (@list) { if ($elem && $elem eq $value_to_find) { $is_in_list = 1; last; } } if ($is_in_list) { ...
Eine etwas andere Version könnte auf sehr langen Listen etwas schneller sein:
my $is_in_list = 0; for (my $i = 0; i < scalar(@list); ++$i) { if ($list[i] && $list[i] eq $value_to_find) { $is_in_list = 1; last; } } if ($is_in_list) { ...
quelle
List :: MoreUtils
Bei Perl> = 5.10 ist der Smart Match Operator sicherlich der einfachste Weg, wie viele andere bereits gesagt haben.
Bei älteren Perl-Versionen würde ich stattdessen List :: MoreUtils :: any vorschlagen .
List::MoreUtils
ist kein Kernmodul (manche sagen, es sollte sein), aber es ist sehr beliebt und in den wichtigsten Perl-Distributionen enthalten.Es hat folgende Vorteile:
in
tut) und nicht den Wert des Elements, wie esList::Util::first
tut (was das Testen schwierig macht, wie oben erwähnt);grep
dazu stoppt es beim ersten Element, das den Test besteht (auch die Kurzschlüsse des Smart Match Operators von Perl ).Hier ist ein Beispiel, das mit jedem gesuchten (skalaren) Wert funktioniert, einschließlich
undef
:use List::MoreUtils qw(any); my $value = 'test'; # or any other scalar my @array = (1, 2, undef, 'test', 5, 6); no warnings 'uninitialized'; if ( any { $_ eq $value } @array ) { print "$value present\n" }
PS
(Im Produktionscode ist es besser, den Umfang von einzuschränken
no warnings 'uninitialized'
).quelle
#Smart-matching-in-detail
Fragment mehr.List::Util
scheint auch zu habenany
.TIMTOWTDI
sub is (&@) { my $test = shift; $test->() and return 1 for @_; 0 } sub in (@) {@_} if( is {$_ eq "a"} in qw(d c b a) ) { print "Welcome in perl!\n"; }
quelle
grep
ist hier hilfreichif (grep { $_ eq $element } @list) { .... }
quelle
List::Util::first
ist wahrscheinlich ein etwas effizienterer Weg, dies zu tun.List::Util::first
kein Drop-In-Ersatz fürgrep
. Da grep hier im skalaren Kontext aufgerufen wird, erhalten Sie die Anzahl der übereinstimmenden Elemente. Wenn SieList::Util::first
stattdessen verwenden, können Sie ein Element abrufen,@list
das als falsch verglichen wird ...Wahrscheinlich
Perl6::Junction
ist der klarste Weg zu tun. Keine XS-Abhängigkeiten, kein Durcheinander und keine neue Perl-Version erforderlich.use Perl6::Junction qw/ any /; if (any(@grant) eq 'su') { ... }
quelle
Dieser Blog-Beitrag beschreibt die besten Antworten auf diese Frage.
Kurz gesagt, wenn Sie CPAN-Module installieren können, sind die besten Lösungen:
if any(@ingredients) eq 'flour';
oder
if @ingredients->contains('flour');
Eine üblichere Redewendung ist jedoch:
if @any { $_ eq 'flour' } @ingredients
was ich weniger klar finde.
Aber bitte nicht die first () Funktion benutzen! Es drückt überhaupt nicht die Absicht Ihres Codes aus. Verwenden Sie nicht den Operator "Smart Match": Er ist defekt. Verwenden Sie weder grep () noch die Lösung mit einem Hash: Sie durchlaufen die gesamte Liste. Während any () stoppt, sobald es Ihren Wert findet.
Weitere Informationen finden Sie im Blog-Beitrag.
PS: Ich antworte für Leute, die in Zukunft die gleiche Frage haben werden.
quelle
if any { $_ eq 'flour' } @ingredients
, aber man muss sich daran erinnernuse List::MoreUtils qw/ any /;
.Sie können in Perl eine ausreichend ähnliche Syntax erreichen, wenn Sie Autoload- Hacking durchführen.
Erstellen Sie ein kleines Paket für das automatische Laden:
package Autoloader; use strict; use warnings; our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my ($method) = (split(/::/, $AUTOLOAD))[-1]; die "Object does not contain method '$method'" if not ref $self->{$method} eq 'CODE'; goto &{$self->{$method}}; } 1;
Dann enthält Ihr anderes Paket oder Hauptskript eine Unterroutine, die das gesegnete Objekt zurückgibt, das von Autoload verarbeitet wird, wenn seine Methode versucht wird, aufgerufen zu werden.
sub element { my $elem = shift; my $sub = { in => sub { return if not $_[0]; # you could also implement this as any of the other suggested grep/first/any solutions already posted. my %hash; @hash{@_} = (); return (exists $hash{$elem}) ? 1 : (); } }; bless($sub, 'Autoloader'); }
Dies lässt Sie mit der Verwendung wie folgt aussehen:
doTask if element('something')->in(@array);
Wenn Sie den Abschluss und seine Argumente neu organisieren, können Sie die Syntax in die andere Richtung ändern, damit er so aussieht, was dem Autobox-Stil etwas näher kommt:
doTask if search(@array)->contains('something');
Funktion, um das zu tun:
sub search { my @arr = @_; my $sub = { contains => sub { my $elem = shift or return; my %hash; @hash{@arr} = (); return (exists $hash{$elem}) ? 1 : (); } }; bless($sub, 'Autoloader'); }
quelle