Cheatah: Perl: geschachtelte Hash's

Hallo allerseits,

ich habe gerade versucht, in Perl Hash's zu schachteln... ist leider komplett in die Hose gegangen! Am besten beschreibe ich es mal konkret:

Aus einem Logfile hole ich lauter Bildschirmauflösungen. Das sind dann z.B. 800x600, 1024x768, aber auch 800x553, 1022x721 u.ä. Dazu habe ich eine Angleichroutine erstellt, die allen Auflösungen die ähnlichste zuordnet. Das speichere ich dann in einem Hash, etwa mit $resolutions{&angleich($res)}++;

Nun will ich aber nicht nur die angeglichenen Auflösungen ausgeben, sondern zu jeder einzelnen angeglichenen die originalen, also:

800x600: 126 mal

  • 800x600: 100 mal
  • 800x553: 26 mal
    1024x768: 809 mal
  • 1024x768: 700 mal
  • 1022x721: 109 mal
    usw.

Aus verschiedenen Gründen muß ich dazu zu der Auflösung 1024x768 ein Hash mit den einzelnen "Unterauflösungen" haben. Ich habe versucht, das ganze als $subresolutions{&angleich($res)}{$res}++; hochzuzählen, aber irgendwie konnte ich danach nicht mehr auf die Werte zugreifen. $subresolutions{$angeglichen} lieferte jedenfalls kein Hash zurück...

Wie muß ich das machen? Muß ich anders hochzählen, oder muß ich anders abfragen? Ich bin für jede Hilfe dankbar!

Cheatah

  1. Hallo!

    ich habe gerade versucht, in Perl Hash's zu schachteln... ist leider komplett in die Hose gegangen!

    Das geht auch nicht in der 'einfachen' Form, wie man es (zuerst) gerne hätte:

    !!! %innerHash = $hash{'key'}; !!! FALSCH

    Hashes sind einfach Listen von Werten, die so interpretiert werden, daß sich key und value abwechseln. Schmeißt man nun einfach einen Hash als Wert in einen anderen, bleibt immer
    die Frage welche Einträge nun zu welchem Wert gehören ...

    Seit Perl 5 hat man aber die Möglichkeit mit Referenzen (Zeiger/Pointer) zu arbeiten. Diese Zeiger
    sind dann Einzelwerte, die ich in einem hash oder array als Werte eintragen kann, um so verschachtelte
    Strukturen zu bilden.
    Eine Referenz erhalte ich, indem ich ein \ vor die Variable stelle:

    $hash{'key'} = %innerHash;

    oder @array, $scalar, \*typeglob ...

    Nun will ich aber nicht nur die angeglichenen Auflösungen ausgeben, sondern zu jeder einzelnen angeglichenen die originalen, also:

    800x600: 126 mal

    »»  - 800x600: 100 mal
    »»  - 800x553: 26 mal

    Ich sehe hier, daß Du neben der gesamt Anzahl auch die einzel Anzahlen haben möchtest.
    Das würde aber heißen, daß Du entweder gleich zwei Summen generierst, oder aber beim
    Auslesen, die entsprechenden Einzelwerte aufaddieren mußt ...

    Aus verschiedenen Gründen muß ich dazu zu der Auflösung 1024x768 ein Hash mit den einzelnen "Unterauflösungen" haben. Ich habe versucht, das ganze als $subresolutions{&angleich($res)}{$res}++; hochzuzählen, aber irgendwie konnte ich danach nicht mehr auf die Werte zugreifen. $subresolutions{$angeglichen} lieferte jedenfalls kein Hash zurück...

    So, hier müssen wir ein bisschen ausholen:
    Wie kommt man an die Werte wieder heran? Ich kann Referenzen in einer skalaren Variable
    halten:

    $innerHashRef = $hash{'key'};

    Dereferenziern kann ich dann ganz einfach, indem ich das entsprechende 'Zeichen' voranstelle:

    %innerHash = %$innerHashRef;

    Wenn man das in einem Rutsch machen möchte, so muß man sich mit geschweiften Klammern
    behelfen:

    %innerHash = %{$hash{'key'}};

    Jetzt wollen wir aber nicht den Hash, sonder einen Wert daraus:

    $innerValue = ${{$hash{'key'}}{'innerKey'};

    Oder etwas Objektorientierter über eine Methode eines Hash-Objektes:

    $innerValue = $hash{'key'}->{'innerKey'};

    Hierfür gibt es auch eine Kurzform:

    $innerValue = $hash{'key'}{'innerKey'};

    Beim Werte hochzählen hast Du also schon den richtigen Weg eingeschlagen. Nur beim
    Auslesen hast Du halt eine Hash-Referenz erhalten :(.

    Hier setzt jetzt noch der Punkt an, daß Du für die Aufsummierung noch etwas machen mußt.
    Man könnte beispielsweise in dem inneren Hash noch ein extra Feld anlegen:

    --------------------------------------------
    while (defined ($zeile = <LOGFILE>)) {
        [...]
        $subresolutions{&angleich($res)}{$res}++;
        $subresolutions{&angleich($res)}{'count'}++;
        [...]
    }
    [...]
    $count = $subresolutions{$angeglichen}{'count'};
    --------------------------------------------

    Ich hoffe, daß ich Dir hiermit helfen könnte... Der Unterschied zwischen Erklären und Verwirren
    ist meißt nicht sehr groß ;-)

    Gruß,
       Jörk

    1. Hi Jörk,

      Ich hoffe, daß ich Dir hiermit helfen könnte... Der Unterschied zwischen Erklären und Verwirren
      ist meißt nicht sehr groß ;-)

      da hast Du recht... ich bin jetzt erst mal verwirrt :-)
      Auf jeden Fall vielen Dank für Deine ausführlichen Erklärungen! Mit Referenzierungen hatte ich zwar schon kurz experimentiert, aber da ich das nicht verstanden hatte, habe ich es bald aufgegeben. Mit Deinen Ausführungen komme ich (hoffentlich) etwas weiter! Und wenn nicht frage ich einfach noch mal ;-)

      Cheatah

      1. Hi Jörk,

        dank Deiner Hilfe habe ich es jetzt geschafft! Ich speichere die Daten via

        $resolution{&nearres($res)}++;
        $realres{&nearres($res)}{$res}++;

        und frage später ab mit zwei Schleifen:

        @aresolution = hsort(%resolution);   # hsort macht aus dem Hash ein Array, wobei Key und Wert vereint mit "*" getrennt sind
        foreach $foo (@aresolution) {
          ($foo1,$foo2) = split(/\*/,$foo);
          ...
          @arealres = hsort(%{$realres{$foo1}});
          foreach $foo3 (@arealres) {
            ...
          }
        }

        So klappt's! Noch mal danke!

        Cheatah