NAME
Quiq::Hash - Zugriffssicherer Hash mit automatisch generierten Attributmethoden
BASE CLASS
SYNOPSIS
Klasse laden:
use Quiq::Hash;
Hash-Objekt instantiieren:
my $h = Quiq::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);
DESCRIPTION
Ein Objekt dieser Klasse repräsentiert einen zugriffssicheren Hash, d.h. einen Hash, dessen Schlüsselvorrat bei der Instantiierung festgelegt wird. Ein lesender oder schreibender Zugriff mit einem Schlüssel, der nicht zum 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 der Klasse wie einer normaler Perl-Hash und kann auch so angesprochen werden. Bei den Methoden ist der konventionelle Zugriff als Alternative Formulierung
angegeben.
METHODS
Instantiierung
new() - Instantiiere Hash
Synopsis
$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]
$h = $class->new(\%hash,@keyVal); # [6]
Description
Instantiiere ein Hash-Objekt, setze die Schlüssel/Wert-Paare und liefere eine Referenz auf dieses Objekt zurück.
- [1]
-
Leerer Hash.
- [2]
-
Die Argumentliste ist eine Aufzählung von Schlüssel/Wert-Paaren.
- [3]
-
Schlüssel und Werte befinden sich in getrennten Arrays. Ist ein Wert
undef
, wird $val gesetzt, falls angegeben. - [4]
-
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. - [5]
-
Blesse den Hash %hash auf Klasse $class.
- [6]
-
Instantiiere den Hash aus den Schlüssel/Wert-Paaren @keyVal und weise dem (restricted) Hash alle Komponenten aus %hash zu. Dieser Aufruf ist nützlich, um einen anonymen Hash zu einem Hash-Objekt mit vorgegebenen Attributen zu machen. (Wenn der anonyme Hash ein nicht-vorgesehenes Attribut enthält, wird eine Exception geworfen.)
fabricate() - Instantiiere Hash für Klasse
Synopsis
$h = $class->fabricate($subClass,...);
Description
Wie new(), nur dass der Hash als Instanz der Subklasse $subClass erzeugt wird. Die Subklasse wird on-the-fly erzeugt, falls sie noch nicht existiert.
Akzessoren
get() - Werte abfragen
Synopsis
$val = $h->get($key);
@vals = $h->get(@keys);
Description
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
getRef() - Referenz auf Wert
Synopsis
$valS = $h->getRef($key);
Description
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.
Alternative Formulierung:
$valS = \$h->{$key};
Example
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);
getArray() - Liefere Array
Synopsis
@arr|$arr = $h->getArray($key);
Description
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.
try() - Werte abfragen ohne Exception
Synopsis
$val = $h->try($key);
@vals = $h->try(@keys);
Description
Wie get(), nur dass im Falle eines unerlaubten Schlüssels keine Exception geworfen, sondern undef
geliefert wird.
set() - Setze Schlüssel/Wert-Paare
Synopsis
$h->set(@keyVal);
Description
Setze die angegebenen Schlüssel/Wert-Paare.
Alternative Formulierung:
$h->{$key} = $val; # ein Schlüssel/Wert-Paar
@{$h}{@keys} = @vals; # mehrere Schlüssel/Wert-Paare
add() - Setze Schlüssel/Wert-Paare ohne Exception
Synopsis
$val = $h->add($key=>$val);
@vals = $h->add(@keyVal);
Description
Wie set(), nur dass im Falle eines unerlaubten Schlüssels keine Exception generiert, sondern der Hash um das Schlüssel/Wert-Paar erweitert wird.
memoize() - Cache Wert auf berechnetem Attribut
Synopsis
$val = $h->memoize($key,$sub);
Description
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
};
memoizeWeaken() - Cache schwache Referenz auf berechnetem Attribut
Synopsis
$ref = $h->memoizeWeaken($key,$sub);
Description
Wie memozize(), nur dass $sub eine Referenz liefert, die von der Methode automatisch zu einer schwachen Referenz gemacht wird.
Bei nicht-existenter Referenz kann die Methode $sub einen Leerstring liefern. Dieser wird auf undef
abgebildet.
compute() - Wende Subroutine auf Schlüssel/Wert-Paar an
Synopsis
$val = $h->compute($key,$sub);
Description
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.
Example
Methode increment() mit apply() realisiert:
$val = $h->compute($key,sub {
my ($h,$key) = @_;
return $h->{$key}+1; # nicht $h->{$key}++!
});
Automatische Akzessoren
AUTOLOAD() - Erzeuge Akzessor-Methode
Synopsis
$val = $h->AUTOLOAD;
$val = $h->AUTOLOAD($val);
Description
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) -or-
$h->name = $name
In der letzten Spalte ("Methoden-Zugriff") steht die Syntax der automatisch generierten Akzessor-Methoden.
Die Akzessor-Methode wird als lvalue-Methode generiert, d.h. die Hash-Komponente kann per Akzessor-Aufruf manipuliert werden. Beispiele:
$h->name = $name;
$h->name =~ s/-//g;
Die Erzeugung einer Akzessor-Methode erfolgt (vom Aufrufer unbemerkt) beim ersten Aufruf. Danach wird die Methode unmittelbar gerufen.
Der Zugriff über eine automatisch generierte Attributmethode ist ca. 30% schneller als über $h->get().
Schlüssel
keys() - Liste der Schlüssel
Synopsis
@keys|$keyA = $h->keys;
Description
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.
Alternative Formulierung:
@keys = keys %$h;
hashSize() - Anzahl der Schlüssel
Synopsis
$n = $h->hashSize;
Description
Liefere die Anzahl der Schlüssel/Wert-Paare des Hash.
Alternative Formulierung:
$n = keys %$h;
validate() - Überprüfe Hash-Schlüssel
Synopsis
$class->validate(\%hash,\@keys);
$class->validate(\%hash,\%keys);
Description
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, wird eine Exception geworfen.
Kopieren
copy() - Kopiere Hash
Synopsis
$h2 = $h->copy;
$h2 = $h->copy(@keyVal);
Description
Kopiere Hash, d.h. instantiiere 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.
join() - Füge Hash hinzu
Synopsis
$h = $h->join(\%hash);
Returns
Hash (für Method Chaining)
Description
Überschreibe die Schlüssel/Wert-Paare in Hash $h mit den Schlüssel/Wert-Paaren aus Hash %hash. Schlüssel/Wert-Paare in Hash $h, die in Hash %hash nicht vorkommen, bleiben bestehen. Enthält %hash einen Schlüssel, der in $h nicht vorkommt, wird eine Exception geworfen.
Example
Ein Hash-Objekt mit vorgegebenen Attributen aus einem anoymen Hash erzeugen. Der anonyme Hash darf weniger, aber nicht mehr Attribute enthalten:
$h = Quiq::Hash->new([qw/
name
label
width
height
/])->join(\%hash);
Löschen
delete() - Entferne Schlüssel/Wert-Paare
Synopsis
$h->delete(@keys);
Description
Entferne die Schlüssel @keys (und ihre Werte) aus dem Hash. An der Menge der zulässigen Schlüssel ändert sich dadurch nichts!
Alternative Formulierung:
delete $h->{$key}; # einzelner Schlüssel
delete @{$h}{@keys}; # mehrere Schlüssel
clear() - Leere Hash
Synopsis
$h->clear;
Description
Leere Hash, d.h. entferne alle Schlüssel/Wert-Paare.
Alternative Formulierung:
%$h = ();
Externe Repräsentation
asString() - Darstellung in einer Zeile
Synopsis
$str = $h->asString;
Alias
asLine()
Description
Liefere den Hash in der Darstellung:
KEY1=>VAL1;KEY2=>VAL2;...
Die Schlüssel sind in alphanumerischer Reihenfolge.
Tests
exists() - Prüfe Schlüssel auf Existenz
Synopsis
$bool = $h->exists($key);
Description
Prüfe, ob der angegebene Schlüssel im Hash existiert. Wenn ja, liefere wahr, andernfalls falsch.
Alternative Formulierung:
$bool = exists $self->{$key};
defined() - Prüfe Wert auf Existenz
Synopsis
$bool = $h->defined($key);
Description
Prüfe, ob der angegebene Schlüssel im Hash einen Wert hat. Wenn ja, liefere wahr, andernfalls falsch.
Alternative Formulierung:
$bool = defined $h->{$key};
isEmpty() - Prüfe auf leeren Hash
Synopsis
$bool = $h->isEmpty;
Description
Prüfe, ob der Hash leer ist. Wenn ja, liefere wahr, andernfalls falsch.
Alternative Formulierung:
$bool = %$h;
Sperren
isLocked() - Prüfe, ob Hash gesperrt ist
Synopsis
$bool = $h->isLocked;
Description
Prüfe, ob der Hash gelockt ist. Wenn ja, liefere wahr, andernfalls falsch.
lockKeys() - Sperre Hash
Synopsis
$h = $h->lockKeys;
Description
Sperre den Hash. Anschließend kann kein weiterer Schlüssel zugegriffen werden. Wird dies versucht, wird eine Exception geworfen.
Alternative Formulierung:
Hash::Util::lock_keys(%$h);
Die Methode liefert eine Referenz auf den Hash zurück.
unlockKeys() - Entsperre Hash
Synopsis
$h = $h->unlockKeys;
Description
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 Instantiierung entsperrt werden:
return Quiq::Hash->new(...)->unlockKeys;
Alternative Formulierung:
Hash::Util::unlock_keys(%$h);
Sonstiges
arraySize() - Größe des referenzierten Arrays
Synopsis
$n = $h->arraySize($key);
setOrPush() - Setze Skalare Komponente oder pushe auf Array-Komponente
Synopsis
$h->setOrPush($key=>$arg);
Arguments
Description
Ist $key eine skalare Komponente des Hash, setze das Attribut auf $arg. Ist $key eine Array-Komponente des Hash, pushe $arg (wenn skalarer Wert) oder @$arg (wenn Array-Referenz) auf das Array.
push() - Füge Werte zu Arraykomponente hinzu
Synopsis
$h->push($key,@values);
Arguments
Description
Füge Werte @values zur Arraykomponente $key hinzu. Die Methode liefert keinen Wert zurück.
unshift() - Füge Element am Anfang zu Arraykomponente hinzu
Synopsis
$h->unshift($key,$val);
Arguments
Description
Füge Wert $val am Anfang zur Arraykomponente $key hinzu. Die Methode liefert keinen Wert zurück.
increment() - Inkrementiere (Integer-)Wert
Synopsis
$n = $h->increment($key);
Description
Inkrementiere (Integer-)Wert zu Schlüssel $key und liefere das Resultat zurück.
Alternative Formulierung:
$n = ++$h->{$key};
addNumber() - Addiere numerischen Wert
Synopsis
$y = $h->addNumber($key,$x);
Description
Addiere numerischen Wert $x zum Wert des Schlüssels $key hinzu und liefere das Resultat zurück.
Alternative Formulierung:
$y = $h->{$key} += $x;
keyVal() - Liste von Schlüssel/Wert-Paaren
Synopsis
@keyVal | $keyValA = $h->keyVal;
Description
Liefere die Liste der Schlüssel/Wert-Paare. Im Skalarkontext liefere eine Referenz auf die Liste.
weaken() - Erzeuge schwache Referenz
Synopsis
$ref = $h->weaken($key);
$ref = $h->weaken($key=>$ref);
Description
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.
Interna
buckets() - Ermittele Bucket-Anzahl
Synopsis
$n = $h->buckets;
Description
Liefere die Anzahl der Hash-Buckets.
bucketsUsed() - Anzahl der genutzten Buckets
Synopsis
$n = $h->bucketsUsed;
Description
Liefere die Anzahl der genutzten Hash-Buckets.
getCount() - Anzahl der get-Aufrufe
Synopsis
$n = $this->getCount;
Description
Liefere die Anzahl der get-Aufrufe seit Start des Programms.
setCount() - Anzahl der set-Aufrufe
Synopsis
$n = $this->setCount;
Description
Liefere die Anzahl der set-Aufrufe seit Start des Programms.
DETAILS
Benchmark
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 - Quiq::Hash: $h->{$k}
F - Quiq::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. 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 Quiq::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 = Quiq::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);
},
});
VERSION
1.220
AUTHOR
Frank Seitz, http://fseitz.de/
COPYRIGHT
Copyright (C) 2024 Frank Seitz
LICENSE
This code is free software; you can redistribute it and/or modify it under the same terms as Perl itself.