Frank Seitz
and 1 contributors

NAME

Prty::Hash - Zugriffssicherer Hash mit automatisch generierten Attributmethoden

BASE CLASS

Prty::Object

SYNOPSIS

Klasse laden:

    use Prty::Hash;

Objekt-Instantiierung:

    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);

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 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.

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]

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 Prty::Hash.

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.

Akzessor-Methoden

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
    };

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 Akzessor-Methoden

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)

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"().

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);

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.

Returns

Hash (für Method Chaining)

Example

Ein Hash-Objekt mit vorgegebenen Attributen aus einem anoymen Hash erzeugen. Der anonyme Hash darf weniger, aber nicht mehr Attribute enthalten:

    $h = Prty::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. An der Menge der zulässigen Schlüssel ändert sich dadurch nichts!

Alternative Formulierung:

    %$h = ();

Anmerkung: Die interne Größe des Hash (Anzahl der allozierten Buckets) wird durch das Leeren nicht verändert.

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.

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.

Alternative Formulierung:

    !Hash::Util::hash_unlocked(%$h);

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 Prty::Hash->new(...)->unlockKeys;

Alternative Formulierung:

    Hash::Util::unlock_keys(%$h);

Sonstiges

arraySize() - Größe des referenzierten Arrays

Synopsis

    $n = $h->arraySize($key);

push() - Füge Element zu Arraykomponente hinzu

Synopsis

    $h->push($key,$val);

Description

Füge Wert $val zur Arraykomponente $key hinzu. Die Methode liefert keinen Wert zurück.

Arguments

$key

Arraykomponente.

$val

Wert, der zum Array am Ende hinzugefügt wird.

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};

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/Vergrößere Bucketanzahl

Synopsis

    $n = $h->buckets;
    $n = $h->buckets($m);

Description

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:

o

$m wird von Perl auf die nächste Zweierpotenz aufgerundet

o

Die Bucketanzahl kann nur vergrößert, nicht verkleinert werden

bucketsUsed() - Anzahl der genutzten Buckets

Synopsis

    $n = $h->bucketsUsed;

Description

Liefere die Anzahl der genutzten Hash-Buckets.

Debugging

debugMode() - Schalte Debug-Modus ein/aus

Synopsis

    $bool = $this->debugMode;
    $bool = $this->debugMode($bool);

Description

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".

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 - 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);
        },
    });

VERSION

1.108

AUTHOR

Frank Seitz, http://fseitz.de/

COPYRIGHT

Copyright (C) 2017 Frank Seitz

LICENSE

This code is free software; you can redistribute it and/or modify it under the same terms as Perl itself.