Michael Schröpl: Dereferenzierung von Pointern auf Hashes

Hi,

ich bin gerade dabei, mir ein package zu basteln, mit dem ich eine bestimmte Klasse von Datensätzen zwischen ihrer Strings-Form (eine Zeile einer Datei) und einer elegant verarbeitbaren Form als Hash hin- und herkonvertieren kann.

Da ich diese Art von Datensätzen mehrfach simultan brauche, brauche ich auch mehrere Hashes.
Also habe ich versucht, einen Pointer auf einen solchen Hash an die entsprechenden Funktionen zu übergeben - VAR-Parameter wie in PASCAL gibt es in Perl ja leider nicht.

Beim Zerlegen klappt das auch prima:
   $$hash_ptr {$key} = $wert
setzt wie gewünscht $wert unter dem Schlüssel $key in den Hash.

Schwieriger wird es überraschenderweise beim Lesen des Hashes. Klar, $$hash_ptr {$key} würde auch hier funktionieren. Ich will aber *alle* Einträge bearbeiten und wieder zu einer Zeile zusammenkleben.
Leider aber funktioniert die folgende Konstruktion nicht:
   foreach my $key (keys $$hash_ptr) { ... }
, weil das "typfreie" Perl nämlich plötzlich meckert, die Funktion "keys" benötige als Parameter einen Hash und keinen dereferenzierten Skalar. Auch wenn da sehr wohl ein Pointer auf einen Hash drinsteht - Perl rafft es offenbar nicht.

Was tun?
Mit der Holzhammermethode habe ich mir jetzt erst mal eine lokale Kopie des Hashes angelegt - *das* funktioniert! - aber performancemäßig ist das eine Katastrophe, wenn ich bloß zur Umgehung einer syntaktischen Schwäche von Perl ständig meine kompletten Daten duplizieren muß (ich weiß noch nicht mal die Zehnerpotenz der erforderlichen Aufrufe ...).

Hat jemand eine Idee, wie ich ohne diese Duplizierung sämtliche Einträge eines über einen Pointer adressierten Hashs verarbeiten kann?
Die Reihenfolge ist mir nicht so wichtig, bei der nächsten Zersplitterung wird ja wieder ein Hash daraus - ich muß bloß alle Einträge erwischen ...

  1. Hallo Michael!

    Also habe ich versucht, einen Pointer auf einen solchen Hash an die entsprechenden Funktionen zu übergeben - VAR-Parameter wie in PASCAL gibt es in Perl ja leider nicht.

    Naja, die Pascalschen VAR-Parameter sind ja nichts anderes als versteckte Pointer, nur dass es in der Syntax der Sprache gekapselt wird. Deshalb habe ich kein Problem damit, wenn eine Sprache kein call-by-reference eingebaut hat, solange sie nur Pointer unterstuetzt (sind eh' viel flexibler).

    Leider aber funktioniert die folgende Konstruktion nicht:
       foreach my $key (keys $$hash_ptr) { ... }

    Stimmt, Du willst hier eine Hash-Referenz zu einem Skalar dereferenzieren - ein astreiner Typennkonflikt, den sogar Perl mitbekommt. Du musst sie zu einem Hash dereferenzieren:
          foreach my $key (keys %$hash_ptr) { ... }

    Hoffe ich hab jetzt nichts falsches gesagt *g*
    So long

    1. Naja, die Pascalschen VAR-Parameter sind ja nichts anderes als versteckte Pointer, nur dass es in der Syntax der Sprache gekapselt wird. Deshalb habe ich kein Problem damit, wenn eine Sprache kein call-by-reference eingebaut hat, solange sie nur Pointer unterstuetzt (sind eh' viel flexibler).

      Aber - wie in meinem Fall - viel komplizierter zu notieren. Ich will nicht in Pointern denken, sondern in meinen eigenen Datentypen.

      Stimmt, Du willst hier eine Hash-Referenz zu einem Skalar dereferenzieren - ein astreiner Typennkonflikt, den sogar Perl mitbekommt. Du musst sie zu einem Hash dereferenzieren:
            foreach my $key (keys %$hash_ptr) { ... }

      Jawoll, das scheint es zu sein - so kann's gehen, wenn man zum ersten Mal einen komplexen Typ zu dereferenzieren versucht ... wieder was gelernt ... nein, die Perl-Doku ist nicht wirklich intuitiv ...

      Hoffe ich hab jetzt nichts falsches gesagt *g*

      Syntaktisch tut es schon mal - ob auch semantisch, das werde ich sehen, wenn die Umgebung fertig ist.
      (Momentan bastele ich erst mal die Bausteine, der "Leim" kommt später - im wesentlichen will ich Paare solcher Dateien miteinander verschmelzen - wobei derzeit jeweils erst mal zwei Hashes pro Zeile eingesetzt werden -, und das aufgrund einer einzulesenden Regeldatei ...).

      Vielen Dank - und toi toi toi für den synchronisierten Poster!
      (Mail mir doch mal, ob Du nur flock() reingemacht hast oder doch irgendwas Heftiges in Richtung Client-Server, wie zuvor andiskutiert ...)

      Ach ja, eine Frage fällt mir in diesem Kontext noch ein: Kann ich ganz "intuitiv" einen Hash of Hashes aufbauen und verwenden? (In Pascal könnte ich solche Fragen alle beantworten - außer Dateien kann man dort alles in alles schachteln.)
      Insbesondere muß ich einen Pointer auf einen der gehashten Hashes (der dann einer Dateizeile entspricht) als Parameter an die erwähnte Funktion übergeben dürfen - und ich habe was gelesen, daß Pointer auf Hash-Elemente irgendwie wegen impliziter Typkonversion in Perl nicht funktionieren soll ... ein Hash of Hashes würde mir vielleicht einiges ersparen, denn bisher beabsichtige ich, ihn zu emulieren (Hash of Strings, und bei Verwendung dann den String mit der Modulfunktion separat hashen und mit der aus der zweiten Datei gerade gelesenen Partner-Zeile verschmelzen).

      1. Huhu

        Aber - wie in meinem Fall - viel komplizierter zu notieren. Ich will nicht in Pointern denken, sondern in meinen eigenen Datentypen.

        Jeder wie er mag. *g*

        Jawoll, das scheint es zu sein - so kann's gehen, wenn man zum ersten Mal einen komplexen Typ zu dereferenzieren versucht ... wieder was gelernt ... nein, die Perl-Doku ist nicht wirklich intuitiv ...

        Wem sagst Du das... ;-(

        Syntaktisch tut es schon mal - ob auch semantisch, das werde ich sehen, wenn die Umgebung fertig ist.
        (Momentan bastele ich erst mal die Bausteine, der "Leim" kommt später - im wesentlichen will ich Paare solcher Dateien miteinander verschmelzen - wobei derzeit jeweils erst mal zwei Hashes pro Zeile eingesetzt werden -, und das aufgrund einer einzulesenden Regeldatei ...).

        Vielen Dank - und toi toi toi für den synchronisierten Poster!
        (Mail mir doch mal, ob Du nur flock() reingemacht hast oder doch irgendwas Heftiges in Richtung Client-Server, wie zuvor andiskutiert ...)

        Nee, das Heftige wollten wir ja noch auf spaeter verschieben. Habe eigentlich nicht mehr gemacht als noetig. Ich schicks Dir nachher mal, wenn ich at home bin.

        Ach ja, eine Frage fällt mir in diesem Kontext noch ein: Kann ich ganz "intuitiv" einen Hash of Hashes aufbauen und verwenden? (In Pascal könnte ich solche Fragen alle beantworten - außer Dateien kann man dort alles in alles schachteln.)

        Ok, kleiner Ausholer:

        @liste = (1, 2, 3);         # Zugriff: $liste[0] == 1
        $listref = [1, 2, 3];       # Zugriff: $listref->[0] oder ${$listref}[0] (vereinfacht: $$listref[0])

        %hash = (
            'key1' => 'value1',     # Zugriff: $hash{'key1'} eq 'value1'
            'key2' => 'value2',
            'key3' => 'value3'
        );

        $hashref = {
            'key1' => 'value1',     # Zugriff: $hashref->{'key1'}
            'key2' => 'value2',     #    oder: ${$hashref}{'key2'} (vereinfacht: $$hashref{'key2'})
            'key3' => 'value3'
        };

        Wie Du siehst, liefern die Operatoren [] bzw. {} jeweils eine Referenz (welche an sich ein Skalar ist) auf eine anonyme Liste bzw. einen anonymen Hash zurueck.

        Wie Du sicher schon gehoert hast, baut man Lists of Lists und Hashes of Hashes dadurch, dass z.B. eine Masterliste lauter Referenzen auf Sub-Listen beinhaltet. Da die [] und {} genau solche Referenzen liefern, kann man also ohne Probleme schreiben:

        @listoflist = (
            [1, 2, 3],              #     Zugriff: ${$listoflist[0]}[0] == 1
            [4, 5, 6],              #        oder: $listoflist[1]->[0]  == 4
            [7, 8, 9]               # vereinfacht: $listoflist[2][0]    == 7
        );

        %hashofhash = (
            subhash1 => {
                'key1' => 'value1', #     Zugriff: ${$hashofhash{'subhash1'}}{'key1'} eq 'value1'
                'key2' => 'value2', #        oder: $hashofhash{'subhash1'}->{'key2'}  eq 'value2'
                'key3' => 'value3'  # vereinfacht: $hashofhash{'subhash1'}{'key3'}    eq 'value3'
            },
            
            subhash2 => {
                'key4' => 'value4',
                'key5' => 'value5',
                'key6' => 'value6'
            },
            
            subhash3 => {
                'key1' => 'value7', # $hashofhash{'subhash3'}->{'key1'} eq 'value7'
                'key2' => 'value8',
                'key3' => 'value9'
            }
        );

        Mehr davon gibt's dann in der perldsc manpage.

        Insbesondere muß ich einen Pointer auf einen der gehashten Hashes (der dann einer Dateizeile entspricht) als Parameter an die erwähnte Funktion übergeben dürfen - und ich habe was gelesen, daß Pointer auf Hash-Elemente irgendwie wegen impliziter Typkonversion in Perl nicht funktionieren soll

        Meinst Du das?
        $sh3ref = $hashofhash{'subhash3'};
        $$sh3ref{'key2'} eq $sh3ref->{'key2'} eq 'value8';

        ... ein Hash of Hashes würde mir vielleicht einiges ersparen, denn bisher beabsichtige ich, ihn zu emulieren (Hash of Strings, und bei Verwendung dann den String mit der Modulfunktion separat hashen und mit der aus der zweiten Datei gerade gelesenen Partner-Zeile verschmelzen).

        Mmh, musst Du sehen, was Dir besser gefaellt. HoH's sind vermutlich speicherintersiver, HoStrings brauchen vielleicht mehr Zeit?

        Kleiner Spass am Rande:
            print( { 1 => 1, 2 => 4, 3 => 9, 4 => 16, 5 => 25 } -> {$number} );
        Wie funktioniert das Ding? ;-)

        Calocybe