Prty::Hash - Zugriffssicherer Hash mit automatisch generierten Attributmethoden
Prty::Object
Klasse laden:
use Prty::Hash;
Objekt-Instanziierung:
my $h = Prty::Hash->new(a=>1,b=>1,c=>3);
Werte abfragen oder setzen:
my $v = $h->get('a'); # oder: $v = $h->{'a'}; $h->set(b=>2); # oder: $h->{'b'} = 2;
Unerlaubte Zugriffe:
$v = $h->get('d'); # Exception! $h->set(d=>4); # Exception!
Erlaubte Zugriffe;
$v = $h->try('d'); # undef $h->add(d=>4);
Ein Objekt dieser Klasse repräsentiert einen Zugriffssicheren Hash, d.h. einen Hash, dessen Schlüsselvorrat bei der Instanziierung festgelegt wird. Ein lesender oder schreibender Zugriff mit einem Schlüssel, der nicht zu dem Schlüsselvorrat gehört, ist nicht erlaubt und führt zu einer Exception.
Der Zugriffsschutz beruht auf der Funktionalität des Restricted Hash.
Abgesehen vom Zugriffsschutz verhält sich ein Hash-Objekt dieser Klasse wie einer normaler Hash und kann auch so angesprochen werden. Bei den Methoden ist der entsprechende konventionelle Zugriff als Alternative Formulierung angegeben.
Alternative Formulierung
$h = $class->new; # [1] $h = $class->new(@keyVal); # [2] $h = $class->new(\@keys,\@vals[,$val]); # [3] $h = $class->new(\@keys[,$val]); # [4] $h = $class->new(\%hash); # [5]
Instanziiere ein Hash-Objekt, setze die Schlüssel/Wert-Paare und liefere eine Referenz auf dieses Objekt zurück.
Leerer Hash.
Die Argumentliste ist eine Aufzählung von Schlüssel/Wert-Paaren.
Schlüssel und Werte befinden sich in getrennten Arrays. Ist ein Wert undef, wird $val gesetzt, falls angegeben.
undef
Nur die Schlüssel sind angegeben. Ist $val angegeben, werden alle Werte auf diesen Wert gesetzt. Ist $val nicht angegeben, werden alle Werte auf undef gesetzt.
Blesse den Hash %hash auf Klasse Prty::Hash.
$val = $h->get($key); @vals = $h->get(@keys);
Liefere die Werte zu den angegebenen Schlüsseln. In skalarem Kontext liefere keine Liste, sondern den Wert des ersten Schlüssels.
Alternative Formulierung:
$val = $h->{$key}; # ein Schlüssel @vals = @{$h}{@keys}; # mehrere Schlüssel
$valS = $h->getRef($key);
Liefere nicht den Wert zum Schlüssel $key, sondern eine Referenz auf den Wert.
Dies kann praktisch sein, wenn der Wert manipuliert werden soll. Die Manipulation kann dann über die Referenz erfolgen und der Wert muss nicht erneut zugewiesen werden.
$valS = \$h->{$key};
Newline an Wert anhängen mit getRef():
$valS = $h->getRef('x'); $$valS .= "\n";
Dasselbe ohne getRef():
$val = $h->get('x'); $val .= "\n"; $val->set(x=>$val);
@arr|$arr = $h->getArray($key);
Liefere die Liste von Werten des Schlüssels $key. Im Skalarkontext liefere eine Referenz auf die Liste (der Aufruf hat dann die gleiche Wirkung wie der Aufruf von $h->get()). Der Wert von $key muss eine Array-Referenz sein.
$val = $h->try($key); @vals = $h->try(@keys);
Wie get(), nur dass im Falle eines unerlaubten Schlüssels keine Exception geworfen, sondern undef geliefert wird.
$h->set(@keyVal);
Setze die angegebenen Schlüssel/Wert-Paare.
$h->{$key} = $val; # ein Schlüssel/Wert-Paar @{$h}{@keys} = @vals; # mehrere Schlüssel/Wert-Paare
$val = $h->add($key=>$val); @vals = $h->add(@keyVal);
Wie set(), nur dass im Falle eines unerlaubten Schlüssels keine Exception generiert, sondern der Hash um das Schlüssel/Wert-Paar erweitert wird.
$val = $h->memoize($key,$sub);
Besitzt das Attribut $key einen Wert, liefere ihn. Andernfalls berechne den Wert mittels der Subroutine $sub und cache ihn auf dem Attribut.
Die Methode ist nützlich, um in Objektmethoden eingebettet zu werden, die einen berechneten Wert liefern, der nicht immer wieder neu gerechnet werden soll.
Alternative Formulierungen:
$val = $h->{$key} //= $h->$sub($key);
oder
$val = $h->{$key} //= do { # Implementierung der Subroutine };
$val = $h->compute($key,$sub);
Wende Subroutine $sub auf den Wert des Schlüssels $key an. Die Subroutine hat die Struktur:
sub { my ($h,$key) = @_; ... return $val; }
Der Rückgabewert der Subroutine wird an Schlüssel $key zugewiesen.
Methode increment() mit apply() realisiert:
$val = $h->compute($key,sub { my ($h,$key) = @_; return $h->{$key}+1; # nicht $h->{$key}++! });
$val = $h->AUTOLOAD; $val = $h->AUTOLOAD($val);
Erzeuge eine Akzessor-Methode für eine Hash-Komponente. Die Methode AUTOLOAD() wird für jede Hash-Komponente einmal aufgerufen. Danach gehen alle Aufrufe für die Komponente direkt an die erzeugte Akzessor-Methode.
Die Methode AUTOLOAD() erweitert ihre Klasse um automatisch generierte Akzessor-Methoden. D.h. für jede Komponente des Hash wird bei Bedarf eine Methode erzeugt, durch die der Wert der Komponente manipuliert werden kann. Dadurch ist es möglich, die Manipulation von Attributen ohne Programmieraufwand nahtlos in die Methodenschnittstelle einer Klasse zu integrieren.
Gegenüberstellung:
Hash-Zugriff get()/set() Methoden-Zugriff -------------------- ----------------------- -------------------- $name = $h->{'name'} $name = $h->get('name') $name = $h->name; $h->{'name'} = $name $h->set(name=>$name) $h->name($name)
In der letzten Spalte ("Methoden-Zugriff") steht die Syntax der automatisch generierten Akzessor-Methoden.
Die Erzeugung einer Akzessor-Methode erfolgt (vom Aufrufer unbemerkt) beim ersten Aufruf. Danach wird die Methode unmittelbar aufgerufen.
Der Zugriff über eine automatisch generierte Attributmethode ist ca. 30% schneller als über $h->get().
@keys|$keyA = $h->keys;
Liefere die Liste aller Schlüssel. Die Liste ist unsortiert. Im Skalarkontext liefere eine Referenz auf die Liste.
Die Reihenfolge der Schlüssel ist undefiniert.
@keys = keys %$h;
$n = $h->hashSize;
Liefere die Anzahl der Schlüssel/Wert-Paare des Hash.
$n = keys %$h;
$class->validate(\%hash,\@keys); $class->validate(\%hash,\%keys);
Prüfe die Schlüssel des Hash %hash gegen die Schlüssel in Array @keys bzw. Hash %keys. Enthält %hash einen Schlüssel, der nicht in @keys bzw. %keys vorkommt, wirf eine Exception.
$h2 = $h->copy; $h2 = $h->copy(@keyVal);
Kopiere Hash, d.h. instanziiere einen neuen Hash mit den gleichen Schlüssel/Wert-Paaren. Es wird nicht rekursiv kopiert, sondern eine "shallow copy" erzeugt.
Sind Schlüssel/Wert-Paare @keyVal angegeben, werden diese nach dem Kopieren per set() auf dem neuen Hash gesetzt.
$h->delete(@keys);
Entferne die Schlüssel @keys (und ihre Werte) aus dem Hash. An der Menge der zulässigen Schlüssel ändert sich dadurch nichts!
delete $h->{$key}; # einzelner Schlüssel delete @{$h}{@keys}; # mehrere Schlüssel
$h->clear;
Leere Hash, d.h. entferne alle Schlüssel/Wert-Paare. An der Menge der zulässigen Schlüssel ändert sich dadurch nichts!
%$h = ();
Anmerkung: Die interne Größe des Hash (Anzahl der allozierten Buckets) wird durch das Leeren nicht verändert.
$bool = $h->exists($key);
Prüfe, ob der angegebene Schlüssel im Hash existiert. Wenn ja, liefere wahr, andernfalls falsch.
$bool = $h->defined($key);
Prüfe, ob der angegebene Schlüssel im Hash einen Wert hat. Wenn ja, liefere wahr, andernfalls falsch.
$bool = defined $h->{$key};
$bool = $->isEmpty;
Prüfe, ob der Hash leer ist. Wenn ja, liefere wahr, andernfalls falsch.
$bool = %$h;
$bool = $h->isLocked;
Prüfe, ob der Hash gelockt ist. Wenn ja, liefere wahr, andernfalls falsch.
Hash::Util::hash_locked(%$h);
$h = $h->lockKeys;
Sperre den Hash. Anschließend kann kein weiterer Schlüssel zugegriffen werden. Wird dies versucht, wird eine Exception geworfen.
Hash::Util::lock_keys(%$h);
Die Methode liefert eine Referenz auf den Hash zurück.
$h = $h->unlockKeys;
Entsperre den Hash. Anschließend kann der Hash uneingeschränkt manipuliert werden. Die Methode liefert eine Referenz auf den Hash zurück. Damit kann der Hash gleich nach der Instanziierung entsperrt werden:
return Prty::Hash->new(...)->unlockKeys;
Hash::Util::unlock_keys(%$h);
$n = $h->arraySize($key);
$h->push($key,$val);
Arraykomponente.
Wert, der zum Array am Ende hinzugefügt wird.
Füge Wert $val zur Arraykomponente $key hinzu. Die Methode liefert keinen Wert zurück.
$n = $h->increment($key);
Inkrementiere (Integer-)Wert zu Schlüssel $key und liefere das Resultat zurück.
$n = ++$h->{$key};
$ref = $h->weaken($key); $ref = $h->weaken($key=>$ref);
Mache die Referenz von Schlüssel $key zu einer schwachen Referenz und liefere sie zurück. Ist eine Referenz $ref als Parameter angegeben, setze die Referenz zuvor.
$n = $h->buckets; $n = $h->buckets($m);
Vergrößere die Bucketanzahl des Hash auf (mindestens) $m. Die Methode liefert die Anzahl der Buckets zurück. Ist kein Parameter angegeben, wird nur die Anzahl der Buckets geliefert.
Anmerkungen:
$m wird von Perl auf die nächste Zweierpotenz aufgerundet
Die Bucketanzahl kann nur vergrößert, nicht verkleinert werden
$n = $h->bucketsUsed;
Liefere die Anzahl der genutzten Hash-Buckets.
$bool = $this->debugMode; $bool = $this->debugMode($bool);
Ist Debug-Modus eingeschaltet, wird bei einem unerlaubten Zugriff via $h->get() oder $h->set() eine Exception mit Stacktrace geworfen. Per Default ist der Debug-Modus ausgeschaltet, um den Zugriffs-Overhead zu verringern. Siehe Abschnitt Benchmark.
$n = $this->getCount;
Liefere die Anzahl der get-Aufrufe seit Start des Programms.
$n = $this->setCount;
Liefere die Anzahl der set-Aufrufe seit Start des Programms.
Anzahl Zugriffe pro CPU-Sekunde im Vergleich zwischen verschiedenen Zugriffsmethoden:
A - Hash: $h->{$k} B - Hash: eval{$h->{$k}} C - Restricted Hash: $h->{$k} D - Restricted Hash: eval{$h->{$k}} E - Prty::Hash: $h->{$k} F - Prty::Hash: $h->get($k) Rate F D B E C A F 1401111/s -- -71% -74% -82% -83% -84% D 4879104/s 248% -- -8% -37% -40% -44% B 5297295/s 278% 9% -- -32% -35% -39% E 7803910/s 457% 60% 47% -- -4% -11% C 8104988/s 478% 66% 53% 4% -- -7% A 8745272/s 524% 79% 65% 12% 8% --
Den Hash via $h->get() zuzugreifen (F) ist ca. 85% langsamer als der einfachste Hash-Lookup (A). Wird auf den Methodenaufruf verzichtet und per $h->{$key} zugegriffen (E), ist der Zugriff nur 11% langsamer. Es ist also ratsam, intern per $h->{$key} zuzugreifen. Per $h->get() können immerhin 1.400.000 Lookups pro CPU-Sekunde ausgeführt werden. Bei nicht-zugriffsintensiven Anwendungen ist das sicherlich schnell genug. Bei eingeschaltetem Debug-Modus halbiert sich diese Anzahl wegen des eval{} in etwa, daher ist der Debug-Modus per Default ausgeschaltet. Siehe Methode $h->debugMode(). Die Anzahl der Aufrufe von $h->get() und $h->set() wird intern gezählt und kann per $class->getCount() und $class->setCount() abgefragt werden.
Das Benchmark-Programm (bench-hash):
#!/usr/bin/env perl use strict; use warnings; use Benchmark; use Hash::Util; use Prty::Hash; my $h1 = {0=>'a',1=>'b',2=>'c',3=>'d',4=>'e',5=>'f'}; my $h2 = Hash::Util::lock_ref_keys({0=>'a',1=>'b',2=>'c',3=>'d',4=>'e',5=>'f'}); my $h3 = Prty::Hash->new({0=>'a',1=>'b',2=>'c',3=>'d',4=>'e',5=>'f'}); my $i = 0; Benchmark::cmpthese(-10,{ A=>sub { $h1->{$i++%5}; }, B=>sub { eval{$h1->{$i++%5}}; }, C=>sub { $h2->{$i++%5}; }, D=>sub { eval{$h2->{$i++%5}}; }, E=>sub { $h3->{$i++%5}; }, F=>sub { $h3->get($i++%5); }, });
1.081
Frank Seitz, http://fseitz.de/
Copyright (C) 2016 Frank Seitz
This code is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install Prty, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Prty
CPAN shell
perl -MCPAN -e shell install Prty
For more information on module installation, please visit the detailed CPAN module installation guide.