Donnerstag, 24. Januar 2008

Perl 5.10: Smart Matching

Der neue Smart-Matching-Operator ~~ ist meiner Ansicht nach die interessanteste Verbesserung von Perl 5.10.

Man kann jetzt sehr einfach nach der berühmten Nadel im Heuhaufen suchen,


use 5.010;

if ( $nadel ~~ $heu ) {
say 'Treffer in String';
}

if ( $nadel ~~ @heu ) {
say 'Treffer in Array';
}

if ( $nadel ~~ %heu ) {
say "Schlüssel $nadel existiert im Hash \%heu";
}

if ( $nadel ~~ $coderef ) {
say 'Codereferenz: $coderef->($nadel) liefert wahr';
}

if ( $nadel ~~ qr/Regex/ ) {
say 'Der reguläre Ausdruck passt';
}

und muss sich dabei nicht um den passenden Vergleichsoperator kümmern.

Schritt für Schritt

Einbinden

Der neue Smart-Matching Operator ~~ steht nur zur Verfügung, wenn er explizit angefordert wird:

use 5.010;

(Siehe dazu auch Perl 5.10: use 5.010;.)

KommutativerOperator

Der Smart-Matching Operator ist ein kommutativer Operator, d.h, es spielt keine Rolle, ob man $nadel ~~ @heu oder @heu ~~ $nadel verwendet. Die beiden Ausdrücke können also nach Belieben vertauscht werden.

~~ wird genauso verwendet wie die bekannten Vergleichsoperatoren eq bzw. ==.

Testprogramm

Um das Verhalten des Smart-Matching besser zu verstehen, erstellen wir zunächst ein sehr einfaches kleines Programm, welches anzeigt, ob bei einem Vergleich ein Treffer erzielt wurde oder nicht.

#!/opt/perl510/bin/perl
use strict;
use warnings;
use 5.010;

sub test_smartmatch {
my $lhs = shift @_; # Left Hand Side (LHS)
my $rhs = shift @_; # Right Hand Side (RHS)
if ( $lhs ~~ $rhs ) {
say "$lhs smartmatches $rhs";
} else {
say "$lhs doesn't smartmatch $rhs";
}
} ##

Und verwenden das Testprogramm für die nachfolgenden Fälle.

Strings vergleichen

&test_smartmatch("Foo","Foo");
Foo smartmatches Foo
&test_smartmatch("Fo","Foo");
Fo doesn't smartmatch Foo

Hier verhält sich ~~ wie eq, dem Vergleichsoperator für Zeichenketten.

Zahlen vergleichen

# Numish (looks like a number)
&test_smartmatch(23, 23);
23 smartmatches 23
&test_smartmatch(23,23.0);
23 smartmatches 23
&test_smartmatch(23,"23.0");
23 smartmatches 23.0
&test_smartmatch(23, "23.0x");
23 doesn't smartmatch 23.0x

Anders als der numerische Vergleichsoperator == handelt ~~ nach dem Motto "Wenn es aussieht wie eine Zahl, dann behandle es auch wie eine Zahl".
Vorsicht:
Wenn beide Seiten des Vergleichs wie ein String aussehen, wird es auch wie ein String behandelt.

&test_smartmatch('23', '23.0');
23 doesn't smartmatch 23.0
&test_smartmatch("23", "23.0");
23 doesn't smartmatch 23.0


Arrays

&test_smartmatch(23, [qw/12 13 23 32 42/]);
23 smartmatches ARRAY(0x3a224)
&test_smartmatch([qw/12 13 23 32 42/], 23);
ARRAY(0x3a104) smartmatches 23

Hier prüft ~~, ob der Array den gesuchten Wert enthält. Das Verhalten entspricht grep.

Auch hier gelten die oben angeführten Vergleichsregeln für Zeichenketten und Zahlen.

Hashes


&test_smartmatch(97, {'a' => '97', 'b' => '98', 'c' => '99' });
97 doesn't smartmatch HASH(0x18ca3c)
&test_smartmatch('a', {'a' => '97', 'b' => '98', 'c' => '99' });
a smartmatches HASH(0x3a314)

Hier prüft ~~, ob der Hash den gesuchten Schlüssel (key) enthält. Das Verhalten entspricht exists.

Reguläre Ausdrücke

&test_smartmatch('Foo',qr/oo/);
Foo smartmatches (?-xism:oo)

Hier verhält sich ~~ wie =~, dem Bindungsoperator für reguläre Ausdrücke.

Datenstrukturen vergleichen

Arrays vergleichen

&test_smartmatch([1,2,3],[1,2,3]);
ARRAY(0x3a314) smartmatches ARRAY(0x190cd3c)
&test_smartmatch([1,2,3],[1,2]);
ARRAY(0x18fa68c) doesn't smartmatch ARRAY(0x19161ac)
&test_smartmatch([1,2,3],[3,2,1]);
ARRAY(0x18fa67c) doesn't smartmatch ARRAY(0x191636c)

~~ prüft hier, ob alle Werte der Arrays identisch sind. Beide Arrays müssen daher auch gleich groß (lang) sein. Die Reihenfolge der Elemente spielt, wie von Arrays gewohnt, eine Rolle.

Hashes vergleichen

&test_smartmatch( {'a' => '97', 'b' => '98', 'c' => '99' },
{'c' => '99', 'b' => '98', 'a' => '97' });
HASH(0x3a104) smartmatches HASH(0x19461bc)
&test_smartmatch( {'a' => '97', 'b' => '98', 'c' => '99' },
{'a' => '100', 'b' => '101', 'c' => '102' });
HASH(0x3a314) smartmatches HASH(0x193cd7c)

~~ prüft hier, ob die Schlüssel der Hashes identisch sind. Beide Hashes müssen daher auch gleich groß sein. Die Reihenfolge der Elemente spielt, wie von Hashes gewohnt, keine Rolle.

Fazit
Smart-matching ist ein wirklich cooles Feature. Es macht einfach Spaß. Wenn man ein wenig mit Zahlen und Zeichenketten aufpasst, macht ~~ das, was man erwartet (DWIM).

Siehe dazu auch:


That's it.

Vorhergehende Artikel von mir zu den neuen Features und Verbesserungen in Perl 5.10:

1 Kommentar:

Andreas Hernitscheck hat gesagt…

Hallo, ich glaube der Abschnitt "Arrays" stimmt nicht.

Das hat bei mir nicht funktioniert.

Nur, wenn das scalar links steht.