Martin H. Sluka <martin@sluka.de>
2003-01-31
Perl bietet die Möglichkeit, das Verhalten seiner vordefinierten Datentypen zu modifizieren, indem man eine Klasse implementiert, in der die verschiedenen Funktionen des jeweiligen Datentyps als Methoden definiert sind, und an die dann einzelne Variablen des jeweiligen Typs mittels tie()
gebunden werden können, vgl. auch die man-Page perltie(1).
Im CPAN sind bereits etliche solcher Klassen zu finden, die viele Anwendungsszenarien abdecken sollten. Dieser Beitrag soll einen kurzen Überblick über die derzeit verfügbaren Klassen speziell für assoziative Arrays (Hashes) geben, mit Schwerpunkt auf drei neue, von mir selbst entwickelte einschlägige Module.
Diese drei von mir entwickelten Module bauen aufeinander auf, d. h. Tie::Hash::Abbrev::Smart ist eine Sub-Klasse von Tie::Hash::Abbrev, das seinerseits eine Sub-Klase von Tie::Hash::Array darstellt. Ursprünglich wollte ich primär die Funktionalität des erstgenannten Moduls implementieren; es kristallisierte sich während der Entwicklung allerdings heraus, dass es sinnvoll sein könnte, die verschiedenen Teil-Funktionalitäten, aus denen sich das Verhalten von Tie::Hash::Array::Smart letztlich zusammensetzt, in eigene Module aufzuspalten, um sie so auch einzeln zugänglich zu machen, und sei es zur Entwicklung weiterer Sub-Klassen.
Hashes, die die an dieses Modul gebunden werden, werden intern nicht als solche, sondern als nach ihren Schlüsseln (keys
) asciibetisch (d. h. mittels cmp
) sortierte Arrays gespeichert; die Suche nach einzelnen Elementen erfolgt somit als binäre Suche, so dass Hash mit n Einträgen in maximal log2n Schritten nach einem bestimmten Element durchsucht werden kann.
Im Vergleich zu normalen Hashes bietet die Verwendung dieses Moduls folgende Vorteile:
each()
und entsprechend auch keys()
und values()
liefern den Inhalt des Hashes in der oben beschriebenen deterministischen Reihenfolge.cmp
-Operator für diese Objekte mittels Overloading entsprechend umdefiniert.Wesentlicher Nachteil gegenüber normalen Hashes ist, dass insbesondere das Einfügen neuer Elemente potenziell "teuer" ist, da es mittels splice()
geschieht.
In der Praxis erwies sich dieser Ansatz jedoch auch für relativ große Hashes (mit mehr als 105 Elementen) als ausreichend schnell und jedenfalls schneller als eine Implementation unter Verwendung von binären Bäumen oder ähnlich komplexen und von Perl nicht nativ unterstützen Datenstrukturen.
Aufbauend auf Tie::Hash::Array ermöglicht dieses Modul es, auf Elemente eines Hashes auch mit abgekürzten Schlüsselnamen zuzugreifen.
Beispielsweise könnte bei einem Hash
use Tie::Hash::Abbrev; tie my %hash, 'Tie::Hash::Abbrev'; %hash = ( sonntag =>0, montag =>1, dienstag=>2, mittwoch =>3, donnerstag=>4, freitag=>5, samstag =>6, sunday =>0, monday =>1, tuesday =>2, wednesday=>3, thursday =>4, friday =>5, saturday=>6 );
auf den Wert des Elements $hash{sonntag}
auch als $hash{so}
zugegriffen werden.
Die verwendete Abkürzung muss allerdings bezogen auf alle Schlüssel des Hashes eindeutig sein; bei $hash{fr}
wäre dies beispielsweise nicht der Fall, da sowohl das Element $hash{freitag}
als auch $hash{friday}
gemeint sein könnte.
In solchen Fällen wird der Zugriff wie der auf ein nicht existierendes Element behandelt, d. h. eine Abfrage des Werts gibt undef
, eine mittels exists()
false zurück.
Zu beachten ist, dass das Löschen eines Elements nur mittels des komplett ausgeschriebenen Schlüssels möglich ist; zum Löschen über abgekürzte Schlüsselwörter steht jedoch eine Zusatzmethode zur Verfügung:
my @deleted = tied(%hash)->delete_abbrev('foo','bar');
Diese löscht alle im Sinne der jeweiligen Klasse eindeutig abgekürzten Elemente und gibt eine Liste der zugehörigen Werte zurück.
Zwar ließe sich ein ähnliches Verhalten, wie diese Klasse es zur Verfügung stellt, auch unter Zuhilfenahme des Standard-Moduls Text::Abbrev erzielen, doch geht's mit diesem Modul ungleich einfacher, abgesehen davon müssen nicht alle potenziellen Abkürzungen bei jeder Änderung des Hashes im Voraus berechnet und im Speicher gehalten werden.
Die bereits bei Tie::Hash::Array erläuterten Features bleiben freilich ebenfalls erhalten.
Auch die Grundidee dieser Klasse ist, Hash-Elemente mit abgekürzten Schlüsselnamen ansprechen zu können.
Anders als bei Tie::Hash::Abbrev gilt eine Abkürzung dabei allerdings auch dann noch als eindeutig, wenn mehrere "passende" Elemente gefunden werden, solange diese Elemente alle den gleichen (String-)Wert haben (oder alle undef
sind).
Bezogen auf das obige Beispiel würde ein Zugriff auf $hash{fr}
oder sogar $hash{f}
also 5
ergeben, da sowohl $hash{freitag}
als auch $hash{friday}
diesen Wert haben.
Disclaimer: Nein, besonders toll finde ich den Namen dieses Moduls auch nicht, aber leider hatte niemand eine bessere Idee geäußert.
Disclaimer: Die folgenden Informationen basieren im Wesentlichen auf den Dokumentationen des jeweiligen Moduls; die Module wurden von mir nicht oder zumindest nicht ausgiebig getestet.
amatch()
aus String::Approx.each()
, keys()
oder values()
die Reihenfolge der zurückgegeben Werte der entspricht, in der die Schlüssel ursprünglich angelegt worden.
Was ich nicht ganz verstanden habe und worauf leider auch der Autor mir eine Antwort schuldig geblieben ist, ist, welchen Vorteil dieses Modul gegenüber dem Standard-Modul Tie::IxHash haben soll.
each()
, keys()
oder values()
wiedergegeben wird, durch Angabe einer Sortier-Routine (wie bei sort()) festzulegen.use strict;
" für Hashes; das Anlegen und Löschen von Elementen wird nur über spezielle Funktionen erlaubt.
Hintergedanke dabei ist primär, Fehler durch Tippfehler bei Hash-Schlüsselnamen vermeiden zu helfen.