Mark: Perl Hashes sortieren

Hallo,

ich habe ein Hash-Array wie folgt:

  
my %ip;  
$ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}+=$bytes_total;  
$ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}{"outgoing_traffic"}+=$bytes_forwards;  
$ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}{"incoming_traffic"}+=$bytes_backwards;  

Dieses wird dynamisch befüllt. Jetzt möchte ich das Hash Array sortieren nach $bytes_total, so dass die Verbindung mit dem höchsten Traffic oben steht. Leider stehe ich komplett auf dem Schlauch.
Danach sollen 5 Verbindungen mit dem meisten Traffic ausgegeben werden.

Probiert habe ich z.B. sowas:

  
my %sorted = sort {$a->[4] <=> $b->[4]} %ip;  

  1. Hi,

    my %sorted = sort {$a->[4] <=> $b->[4]} %ip;

      
    Hashes sind immer unsortiert. Siehe z.B. perldoc perldata:  
    Hashes are unordered collections of scalar values indexed by their  
    associated string key.  
      
    Bis die Tage,  
    Matti
    
    -- 
    [Webapplikationen in C++ entwickeln](http://tntnet.org/)
    
    1. Hi,

      Hashes sind immer unsortiert. Siehe z.B. perldoc perldata:
      Hashes are unordered collections of scalar values indexed by their
      associated string key.

      Bis die Tage,
      Matti

      Ok, dann müsste ich die Daten in ein Array schreiben? Wie würde das aussehen, wenn ich die Ordnung beibehalten möchte?

      Gruß

      1. Hi,

        Ok, dann müsste ich die Daten in ein Array schreiben? Wie würde das aussehen, wenn ich die Ordnung beibehalten möchte?

        Es reicht, wenn du einen Array mit den Schlüsseln deines Hashes in der gewünschten Reihenfolge hast. Du kannst also eine Vergleichsoperator auf den Schlüsseln schreiben, der auf deinen Hash zugreift. Beispiel:

          
        my %myhash;  
        # ...  
        my @sortedkeys = sort { $myhash{$a}{blablub} <=> $myhash{$b}{blablub} } keys %myhash;  
        
        

        Hab lang kein perl mehr programmiert, können also Syntax-Fehler drin sein, aber das Prinzip dürfte klar sein.

        Bis die Tage,
        Matti

        1. Hi,

          Ok, dann müsste ich die Daten in ein Array schreiben? Wie würde das aussehen, wenn ich die Ordnung beibehalten möchte?

          Es reicht, wenn du einen Array mit den Schlüsseln deines Hashes in der gewünschten Reihenfolge hast. Du kannst also eine Vergleichsoperator auf den Schlüsseln schreiben, der auf deinen Hash zugreift. Beispiel:

          my %myhash;

          ...

          my @sortedkeys = sort { $myhash{$a}{blablub} <=> $myhash{$b}{blablub} } keys %myhash;

          
          >   
          > Hab lang kein perl mehr programmiert, können also Syntax-Fehler drin sein, aber das Prinzip dürfte klar sein.  
          >   
          > Bis die Tage,  
          > Matti  
            
          Hallo,  
            
          müsste ich es nicht so schreiben, damit ich nach den total\_bytes sortiere (5. Spalte)?:  
            
          `my @sortedkeys = sort { $a->[4] <=> $b->[4] } keys %ip;`{:.language-perl}  
            
          Was mache ich dann mit dem erhaltenen array, Wenn ich meine Liste sortiert ausgeben möchte?  
            
          ~~~perl
          	  
          foreach(@sortedkeys){ ??? }
          
          1. Hi,

            müsste ich es nicht so schreiben, damit ich nach den total_bytes sortiere (5. Spalte)?:
            my @sortedkeys = sort { $a->[4] <=> $b->[4] } keys %ip;

            $a und $b sind jeweils Einträge aus deinem Hash. In deinem Hash hat %ip nur einen Eintrag, da ist also nichts zu sortieren. Du willst also nur einen Unterhash sortieren. Vielleicht extrahierst du den mal und gehst das Thema dann mal Schritt für Schritt durch.

            Was mache ich dann mit dem erhaltenen array, Wenn ich meine Liste sortiert ausgeben möchte?
            foreach(@sortedkeys){ ??? }

            In der Schleife kannst du entsprechend auf das Hash-Element mit dem Index $_ zugreifen. Das nächste Element ist also $ip{$_}. Wenn du einen Unter-Hash sortieren willst, dann musst du das dann innerhalb der Schleife tun.

            Bis die Tage,
            Matti

            1. Hi,

              müsste ich es nicht so schreiben, damit ich nach den total_bytes sortiere (5. Spalte)?:
              my @sortedkeys = sort { $a->[4] <=> $b->[4] } keys %ip;

              $a und $b sind jeweils Einträge aus deinem Hash. In deinem Hash hat %ip nur einen Eintrag, da ist also nichts zu sortieren. Du willst also nur einen Unterhash sortieren. Vielleicht extrahierst du den mal und gehst das Thema dann mal Schritt für Schritt durch.

              Was mache ich dann mit dem erhaltenen array, Wenn ich meine Liste sortiert ausgeben möchte?
              foreach(@sortedkeys){ ??? }

              In der Schleife kannst du entsprechend auf das Hash-Element mit dem Index $_ zugreifen. Das nächste Element ist also $ip{$_}. Wenn du einen Unter-Hash sortieren willst, dann musst du das dann innerhalb der Schleife tun.

              Bis die Tage,
              Matti

              Hallo,

              das ganze mit foreach durchzurattern ist nicht das Problem:

              	foreach $key_src_ip (keys %{$ip{"source"}}){  
              	    foreach $key_src_port (keys %{$ip{"source"}{$key_src_ip}}){  
              		foreach $key_dest_ip (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}}){  
              		    foreach $key_dest_port (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}}){  
              		    }	  
              		}	  
              	    }  
              	}
              

              Es stellt sich mir nur die Frage, wie man jetzt in der letzten Spalte  nach dem höchsten Wert sortiert und irgendwie den kompletten Key-Pfad dorthin erhält.

              Gruß

              1. Hallo,

                das ganze mit foreach durchzurattern ist nicht das Problem:

                foreach $key_src_ip (keys %{$ip{"source"}}){  
                

                foreach $key_src_port (keys %{$ip{"source"}{$key_src_ip}}){
                foreach $key_dest_ip (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}}){
                    foreach $key_dest_port (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}}){
                    }
                }
                    }
                }

                
                >   
                > Es stellt sich mir nur die Frage, wie man jetzt in der letzten Spalte  nach dem höchsten Wert sortiert und irgendwie den kompletten Key-Pfad dorthin erhält.  
                >   
                > Gruß  
                  
                Noch mal als Anhang ein Dump meines Hashes:  
                  
                $VAR1 = '10.30.46.5';  
                $VAR2 = {  
                          '123' => {  
                                     '172.16.144.21' => {  
                                                          '918' => 34,  
                                                          '919' => 78  
                                                        }  
                                   },  
                          'nfs' => {  
                                     '172.16.144.21' => {  
                                                          '919' => 110  
                                                        },  
                                     '172.16.144.20' => {  
                                                          '919' => 55  
                                                        }  
                                   }  
                        };  
                $VAR3 = '10.30.46.2';  
                $VAR4 = {  
                          'nfs' => {  
                                     '172.16.144.20' => {  
                                                          '933' => 56  
                                                        }  
                                   }  
                        };  
                $VAR5 = '10.30.46.1';  
                $VAR6 = {  
                          'nfs' => {  
                                     '172.16.144.20' => {  
                                                          '919' => 1  
                                                        }  
                                   }  
                        };  
                $VAR7 = '172.16.144.20';  
                $VAR8 = {  
                          '51290' => {  
                                       '10.30.46.3' => {  
                                                         'http-alt' => 1077767  
                                                       }  
                                     }  
                        }  
                  
                Davon möchte ich z.B. die 2 Verbindungen mit den höchsten Werten haben. Also:  
                1\. 172.16.144.20:51290 <-> 10.30.46.3:http-alt 1077767  
                2\. 10.30.46.5:nfs      <-> 172.16.144.21:919   110  
                  
                Gruß
                
                1. my %traffic_map;  
                  foreach my $key_src_ip (keys %{$ip{"source"}}) {  
                      foreach my $key_src_port (keys %{$ip{"source"}{$key_src_ip}}) {  
                          foreach my $key_dest_ip (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}}) {  
                              foreach my $key_dest_port (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}}) {  
                                  push @{$traffic_map{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}{$key_dest_port}}},  
                                      [$key_src_ip,$key_src_port,$key_dest_ip,$key_dest_port];  
                              }  
                          }  
                      }  
                  }  
                    
                  my $top = 0;  
                  for my $traffic (sort {$b <=> $a} keys %traffic_map) {  
                      last if 2 == $top;  
                      for my $src_dst (@{$traffic_map{$traffic}}) {  
                          say sprintf '%20s:%-15s <-> %20s:%-15s %15d', @{$src_dst}, $traffic;  
                          $top++;  
                      }  
                  }  
                  
                  

                  Die zusätzliche Arrayreferenz ist dafür da, falls mehrere src_dst dieselbe Trafficzahl aufweisen.

                  1. my %traffic_map;

                    foreach my $key_src_ip (keys %{$ip{"source"}}) {
                        foreach my $key_src_port (keys %{$ip{"source"}{$key_src_ip}}) {
                            foreach my $key_dest_ip (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}}) {
                                foreach my $key_dest_port (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}}) {
                                    push @{$traffic_map{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}{$key_dest_port}}},
                                        [$key_src_ip,$key_src_port,$key_dest_ip,$key_dest_port];
                                }
                            }
                        }
                    }

                    my $top = 0;
                    for my $traffic (sort {$b <=> $a} keys %traffic_map) {
                        last if 2 == $top;
                        for my $src_dst (@{$traffic_map{$traffic}}) {
                            say sprintf '%20s:%-15s <-> %20s:%-15s %15d', @{$src_dst}, $traffic;
                            $top++;
                        }
                    }

                    
                    > Die zusätzliche Arrayreferenz ist dafür da, falls mehrere src\_dst dieselbe Trafficzahl aufweisen.  
                      
                    Hallo,  
                      
                    vielen vielen Dank. Kann es zwar erst Morgen testen, beim darüberschauen sah es aber recht gut aus und ich habe es auch verstanden. Ich hatte die ganze Zeit nur auf dem Schlauch gestanden, da ich dachte es gibt eine elegantere Methode in Perl zum sortieren von anonymen Hashes (z.B. eine Funktion, der man nur die Spalte gibt, nach der man sortieren möchte). So ist es ja schon recht umständlich, dafür aber leicht nachzuvollziehen :-)  
                      
                    Gruß  
                      
                    Mark
                    
                    1. Hallo,

                      nochmal abschließend zur Programmierung. Der Code hat bei mir fast auf anhieb geklappt.
                      Das say Feature ist bei mir nicht aktiviert gewesen, also print eignefügt und die Abbruchbedingung für die Schleife war nicht ganz sauber, da die innere Schleife nicht verlassen wird, wenn $top=2 ist und er somit $top hochzählt. Dann ist auch die Abbruchbedingung in der Hauptschleife nicht mehr erfüllt.

                      So also richtig:

                      	    my %traffic_map;  
                      	    foreach my $key_src_ip (keys %{$ip{"source"}}) {  
                      		foreach my $key_src_port (keys %{$ip{"source"}{$key_src_ip}}) {  
                      		    foreach my $key_dest_ip (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}}) {  
                      			foreach my $key_dest_port (keys %{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}}) {  
                      			    push @{$traffic_map{$ip{"source"}{$key_src_ip}{$key_src_port}{$key_dest_ip}{$key_dest_port}}},  
                      				[$key_src_ip,$key_src_port,$key_dest_ip,$key_dest_port];  
                      			}  
                      		    }  
                      		}  
                      	    }  
                        
                      	    my $top = 0;  
                      	    for my $traffic (sort {$b <=> $a} keys %traffic_map) {  
                      		last if 2 == $top;  
                      		for my $src_dst (@{$traffic_map{$traffic}}) {  
                      		    print sprintf ('%20s:%-15s <-> %20s:%-15s %15d', @{$src_dst}, $traffic)."\n";  
                      		    print $top."\n";  
                      		    $top++;  
                      		    last if 2 == $top;  
                      		}  
                      	    }
                      

                      Vielen Dank nochmals.

  2. hi,

    Du hast mit Deinem Hash ein anderes Problem, schalte mal ein

      
    use strict;  
    use warnings;  
    
    

    Der Fehler liegt darin begründet, dass mit

      
    $ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}+=$bytes_total;  
    
    

    ein Wert $bytes_total zugewiesen wird, obwohl an Key $dest_port

      
    ...{$dest_port}{"outgoing_traffic"}  
    
    

    eine Hash-Referenz erwartet wird. Deine Datenstruktur ist überarbeitungsbedürftig.

    Hotti

    1. hi,

      Du hast mit Deinem Hash ein anderes Problem, schalte mal ein

      use strict;
      use warnings;

      
      >   
      > Der Fehler liegt darin begründet, dass mit  
      > ~~~perl
        
      
      > $ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}+=$bytes_total;  
      > 
      
      

      ein Wert $bytes_total zugewiesen wird, obwohl an Key $dest_port

      ...{$dest_port}{"outgoing_traffic"}

      
      >   
      > eine Hash-Referenz erwartet wird. Deine Datenstruktur ist überarbeitungsbedürftig.  
      >   
      > Hotti  
        
      Warum? Meine Tabelle soll z.B. so aussehen:  
        
      "Source" 10.0.0.1, 5554, 10.0.0.2, 80 => 12345, outgoing\_traffic => 12300  
                                                      incoming\_traffic => 45  
      
      1. hi,

        eine Hash-Referenz erwartet wird. Deine Datenstruktur ist überarbeitungsbedürftig.

        Hotti

        Warum? Meine Tabelle soll z.B. so aussehen:

        "Source" 10.0.0.1, 5554, 10.0.0.2, 80 => 12345, outgoing_traffic => 12300
                                                        incoming_traffic => 45

        Zweifelsfrei ein erstrebenswertes Ziel. Nutze strict; und warnings; um Dir den Weg zu diesem Ziel zu erleichtern.

        Z.B.: Beim Sortieren unerwartete Ergebnisse? Passiert gerne, wenn in einer Referenz kein definierter Wert liegt und gerade solch ein Wert als Sortierkriterium genommen wird ;)

        Warum auch nicht das hier:

          
        $SIG{__WARN__} = sub{  
        	my $mesg = shift;  
        	die "Warnung: $mesg";  
        };  
        
        

        Nutze Perls Exception-Model!

        Hotti

        --
        Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
        1. Hallo,

          ich nutze strict und warnings und habe keine Probleme.

          Ich habe ein File, in dem mehrere verbindungen gespeichert sind.

          z.B.

            
          Src       Src_prt     Dest       Dest_prt  bytes->   bytes<- bytes_total  
            
          10\.0.0.1  533         10.0.0.2   80        10        20      30  
          10\.0.0.1  533         10.0.0.2   80        20        50      70
          

          Durch das Hash-Array möchte ich gewährleisten, dass Verbindungen über gleiche IPs und Ports aufaddiert werden und nicht doppelt angelegt werden oder ich manuell in einem Array auf Vorhandensein prüfen muss.
          Genau diese Funktion wird auch erfüllt.

          Gruß

          1. Hallo,

            ich nutze strict und warnings und habe keine Probleme.

            Du hast sowas:

              
            $, = $\ = "\n";  
            my %ip = ();  
            $ip{foo}{bar} = 2;               # Versuch Zuweisung Literal  
            $ip{foo}{bar}{baz} = {bar => 1}; # Key baz hat eine Referenz als Wert  
              
            # Warung:  
            # Can't use string ("2") as a HASH ref while "strict refs" in use at ...  
            
            

            Die Warnung kommt mit use strict; use warnings;
            Es sei denn Du schaltest

            no strict "refs";

            Was ich nicht für empfehlenswert halte.

            Hotti

            1. Hallo,

              ich nutze strict und warnings und habe keine Probleme.

              Du hast sowas:

              $, = $\ = "\n";
              my %ip = ();
              $ip{foo}{bar} = 2;               # Versuch Zuweisung Literal
              $ip{foo}{bar}{baz} = {bar => 1}; # Key baz hat eine Referenz als Wert

              Warung:

              Can't use string ("2") as a HASH ref while "strict refs" in use at ...

              
              >   
              > Die Warnung kommt mit use strict; use warnings;  
              > Es sei denn Du schaltest  
              >   
              > `no strict "refs";`{:.language-perl}  
              >   
              > Was ich nicht für empfehlenswert halte.  
              >   
              > Hotti  
              >   
              >   
                
              Hallo,  
                
              Wie schon geschrieben, habe ich keine warnings explizit ausgeschaltet und benutze use strict; use warnings;  
              M.E. mache ich auch nicht den Fehler den du beischreibst, da "outgoing\_traffic" und "incoming\_traffic" lediglich feste Keys sind, denen ich einen Value zuordne. Soweit ich Hashtables verstehe kann man einem Schlüssel auch immer einen Wert zuordnen. Sprich ich könnte, "Source", $src\_ip", "src\_port"... jeweils einen festen Wert geben. Oder habe ich prinzipiell einen Denkfehler? Vor allem kann ich ja auch auf den Wert des "outgoing\_traffic" zugreifen ohne Fehler.  
                
              Gruß
              
              1. Hallo,

                ich nutze strict und warnings und habe keine Probleme.

                Du hast sowas:

                $, = $\ = "\n";
                my %ip = ();
                $ip{foo}{bar} = 2;               # Versuch Zuweisung Literal
                $ip{foo}{bar}{baz} = {bar => 1}; # Key baz hat eine Referenz als Wert

                Warung:

                Can't use string ("2") as a HASH ref while "strict refs" in use at ...

                
                > >   
                > > Die Warnung kommt mit use strict; use warnings;  
                > > Es sei denn Du schaltest  
                > >   
                > > `no strict "refs";`{:.language-perl}  
                > >   
                > > Was ich nicht für empfehlenswert halte.  
                > >   
                > > Hotti  
                > >   
                > >   
                >   
                > Hallo,  
                >   
                > Wie schon geschrieben, habe ich keine warnings explizit ausgeschaltet und benutze use strict; use warnings;  
                > M.E. mache ich auch nicht den Fehler den du beischreibst, da "outgoing\_traffic" und "incoming\_traffic" lediglich feste Keys sind, denen ich einen Value zuordne. Soweit ich Hashtables verstehe kann man einem Schlüssel auch immer einen Wert zuordnen. Sprich ich könnte, "Source", $src\_ip", "src\_port"... jeweils einen festen Wert geben. Oder habe ich prinzipiell einen Denkfehler? Vor allem kann ich ja auch auf den Wert des "outgoing\_traffic" zugreifen ohne Fehler.  
                >   
                > Gruß  
                  
                Hallo,  
                  
                jetzt weiß ich was du meinst :-) Habe es gemerkt, da mein Programm sich in einem bestimmten Fall komisch verhalten hat, der im Regelfall so gut wie nicht auftritt. Habe meine Struktur überarbeitet, wie folgt:  
                  
                ~~~perl
                		  $ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}{$bytes_total}{"outgoing_traffic"}+=$bytes_forwards;  
                		  $ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}{$bytes_total}{"incoming_traffic"}+=$bytes_backwards;
                

                Musste zwar auch etwas meine Schleifen anpassen, jetzt passt es aber.

                Trotzdem komisch, dass sich Perl nicht beschwert hat.

                Gruß

                1. moin,

                  Trotzdem komisch, dass sich Perl nicht beschwert hat.

                  Beim Arbeiten mit Referenzen gibt es Fälle, in denen Perl nicht meckert und der Fehler nicht bemerkt wird. Bei sehr umfangreichen Scripts sind die Fehlermeldungen manchmal auch irreführend ;)

                  Trotzdem: Lieber eine Warnung mehr beachten als eine zuwenig.

                  Viel Erfolg,
                  Hotti

        2. Warum auch nicht das hier:

          Weil's Murks ist.

          Man schreibt
              use warnings FATAL => 'all';
          Siehe http://perldoc.perl.org/perllexwarn.html#Fatal-Warnings.

          Kann das Forum nicht mal fachlich unhilfreich einführen?

          1. Warum auch nicht das hier:
            Weil's Murks ist.

            Nein im Gegenteil. Ein

              
            $SIG{__WARN__} = sub{  
            	die "Warnung: @_";  
            };  
            
            

            mag zwar etwas altmodisch erscheinen, ist aber sehr hilfreich, vorausgesetzt, Du kannst mit Exceptions richtig umgehen. D.h., es ist vor jeder Ausgabe auf STDOUT auf Exception zu prüfen und so bekommst Du mit o.g. Code jede Warnung mit, die bei einem CGI-Script oder mod_perl, stillschweigend das Error_Log zumüllen würden.

            Weitere Möglichkeiten, falls ein Backtrace gewünscht ist, bietet das Perl-Modul Carp qw(cluck confess).

            Und Überhaupt: Das Exception-Model in Perl ermöglicht Fehlerbehandlungen, die über Kontrollstrukturen sehr aufwändig oder gar nicht zu machen sind. Ein Beispiel:

              
            		if( ($d != $inday) || ($m != $inmonth) || ($y != $inyear) ){  
            			die "Nach Greg. Kalender kein Schaltjahr\n" if( ($inday == 29) && ($inmonth == 2)  );  
            			die "Datum nach Greg. Kalender ungültig\n";  
            		}  
            
            

            Der Code ist Bestandteil einer Objekt-Initialisierung, welche im Konstruktor auf Exception geprüft wird. In der Anwendung, wo das Objekt aus Benutzereingaben initialisiert wird, steht der Fehler in $@, fertig.

            Man schreibt

            Was glaubst Du wohl, wer sich mit 'Man' angesprochen fühlt!?

            1. Tach,

              Und Überhaupt: Das Exception-Model in Perl ermöglicht Fehlerbehandlungen, die über Kontrollstrukturen sehr aufwändig oder gar nicht zu machen sind. Ein Beispiel:

              Exceptions sind nicht dazu da, Geschäftslogik zu bauen, dafür ist der gesamte Prozess viel zu aufwendig. „an exception is the breaching of predefined assumption of the application“ – Exception Handling: Best Practices

              mfg
              Woodfighter

              1. Hi lieber Woodfighter,

                Exceptions sind nicht dazu da, Geschäftslogik zu bauen, dafür ist der gesamte Prozess viel zu aufwendig. „an exception is the breaching of predefined assumption of the application“ – Exception Handling: Best Practices

                Darüber könnten wir ewig streiten ;)
                Fakt ist jedoch: Zur Fehlerbehandlung in Perl-DBI wird RaiseError nach wie vor empfohlen und genau das läuft auf eine Exception.

                Aufwändiger Prozess? Nein, im Gegenteil: Aufwändige Kontrollstrukturen entfallen, der Code wird übersichtlicher!

                Mein Framework wo ich grad dranbin hat folg. Struktur:

                • jeder Req.URL ruft genau eine Methode (n:1 Beziehung Req:Meth)
                • es gibt mehrere Methoden, je nach Aufgabenstellung
                • jede dieser Methoden erzeugt/verändert den Content (auszugebender HTTP-Message-Body für die Response)
                • jede Methode wird beim Ausführen auf Exception getestet, das sind nur wenige Zeilen Code am Ende der Anwendung!

                Wenn in einer der Methoden eine Exception auftritt, führt das NICHT zu einem fatalerror sondern die gesamte Anwendung bleibt in einem stabilen und definierten Zustand.

                Der Exception-ErrCode wird im Browser ausgegeben, zum Debuggen kann ich den Trace ausgeben, für den produktiven Einsatz schalte ich den Trace einfach ab.

                Ich darf in den Methoden selbst Exceptions mit eigens definierten ErrCode werfen, wenn es nicht mehr möglich ist, Eingabefehler über Kontrollstrukturen abzufangen, es wird einfach eine Exception geworfen womit weiterer in der Methode notierter Code nicht mehr ausgeführt wird, die Methode stirbt, nicht jedoch die Application.

                Einfacher gehts nicht ;)

                Hotti

                --
                Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
                1. Hi lieber Woodfighter,

                  Exceptions sind nicht dazu da, Geschäftslogik zu bauen, dafür ist der gesamte Prozess viel zu aufwendig. „an exception is the breaching of predefined assumption of the application“ – Exception Handling: Best Practices

                  Darüber könnten wir ewig streiten ;)

                  Ne, is ja genau das, was ich mache ;)

                  --Hotti

                2. Hi,

                  Exceptions sind nicht dazu da, Geschäftslogik zu bauen
                  Fakt ist jedoch: Zur Fehlerbehandlung in Perl-DBI wird RaiseError nach wie vor empfohlen und genau das läuft auf eine Exception.

                  bleiben wir mal bei diesem Beispiel.
                  Wann treten denn mal Datenbank-Fehler auf:
                  (a) Statement ist fehlerhaft.
                  Sollte nur während der Entwicklung auftreten. Ich habe aber auch noch nie Geschäftslogik gesehen, die Bugs behandelt (vielleicht sollte ich hier eher "selten" denn "nie" sagen :))
                  (b) Datenbank ist nicht erreichbar.
                  Das ist sicherlich kein normaler Geschäftsfall. Muss (klar) abgefangen werden, aber Geschäftslogik ist das nicht.
                  (c) Es wird "auf Verdacht" etwas in die Datenbank geschrieben, wo mit einem Fehler gerechnet wird. Beispielsweise wird einfach versucht, etwas in eine Spalte einzufügen, wo dann etwa ein UNIQUE-Constraint fehlschlägt. Dies wäre Geschäftslogik, aber der Fall wird dann immer lokal behandelt und die Exception eher selten groß durch die Applikation gejagt.

                  Insofern ist der Regelfall Fall (b) und Exceptions für (c) zu nutzen ist eher ein "Abfallprodukt", da man dort i.d.R. nur braucht, dass es fehlgeschlagen ist.

                  Fallen dir mehr Fälle ein?

                  Bis die Tage,
                  Matti

                  1. hi Matti,

                    (a) Statement ist fehlerhaft.
                    Fallen dir mehr Fälle ein?

                    Ja, (a) ist bereits ein "kleiner Denkfehler" Deinerseits: Es gibt Transaktionen, die aus _mehreren_ Statements bestehen können und nicht nur das: Es können auch Drittanbindungen zu einer Transaktion gehören.

                    Anderer Fall, wo Exceptions hilfreich sind: Die Tage habe ich ein Modul geschrieben für Kalenderbechnungen. Zu möglichen Eingabefehlern (Datum) habe ich nicht weniger als 37 verschiedene Fehlermeldungen, die innerhalb des Moduls an sehr verschiedenen Stellen auftreten können. Dies über Kontrollstrukturen abzuhandeln ist einfach nicht mehr möglich, da hilft eine erlösende Exception, wobei selbstverständlich vor der Rückgabe des Objekts evaluiert wird, also dieses try/catch (throw exception) wird nur im Konstruktor gemacht!

                    Hotti

                    1. Hi,

                      (a) Statement ist fehlerhaft.
                      Fallen dir mehr Fälle ein?
                      Ja, (a) ist bereits ein "kleiner Denkfehler" Deinerseits: Es gibt Transaktionen, die aus _mehreren_ Statements bestehen können und nicht nur das: Es können auch Drittanbindungen zu einer Transaktion gehören.

                      Die Frage war, in welchen Fällen Exceptions durch die Datenbank geworfen werden. Alleine durch das Vorkommen von Transaktionen sehe ich noch keine Exceptions (Deadlocks mal ausgenommen). Jedes einzelne Statement für sich ist nur in den Fällen (a) und (c) kaputt, ein anderes Beispiel hast du mir nicht nennen können.
                      Zu Drittanbindungen: du hast explizit Datenbank-Exceptions als Beispiel herangezogen, ich habe mich explizit nur darauf bezogen.

                      Bis die Tage,
                      Matti

                      1. hi,

                        Die Frage war, in welchen Fällen Exceptions durch die Datenbank geworfen werden.

                        Exceptions wirft der Layer, nicht die DB (wäre ja noch schöner, wenn der MySQL-Server in Quierschied mein Script abwürgt, was auf einem Server in Saarbrücken läuft *G). In Perl kannst Du den Layer anweisen, eine Exception zu  werfen oder keine Exception zu werfen und das bereits für den ersten Schritt: Dem Connect.

                        Alleine durch das Vorkommen von Transaktionen sehe ich noch keine Exceptions (Deadlocks mal ausgenommen). Jedes einzelne Statement für sich ist nur in den Fällen (a) und (c) kaputt, ein anderes Beispiel hast du mir nicht nennen können.

                        Freilich kannst Du nach jedem einzelnen Statement auch ohne eine Exception auszulösen, prüfen, ob ein Fehler aufgetreten ist. Betrachten wir folg. Statements einer kleinen Transaktion:

                        ST1: Übernimmt die Daten aus dem Warenkorb (eine Tabelle)
                        ST2: Schreibt die Daten in die Bestellung (zwei Tabellen)
                        ST3: Löscht den Warenkorb (Where=Session)
                        -> Commit, wenn alle 3 fehlerfrei sind

                        Eine Ex. tritt nirgendwo auf, wenn das nicht vereinbart wurde. Du musst jedoch in diesem Fall nach jedem einzelnen ST prüfen, ob es erfolgreich war, das ist bei 3 ST noch (!) überschaubar, aber es geht einfacher:

                        Setze RaiseError = 1; # erhebe jeden Fehler in den Status einer Exception

                        Im try-Block (eval in Perl) dürfen nun alle drei ST unmittelbar aufeinanderfolgend notiert werden. Sofern bei _einem_ ST ein Fehler auftritt, werden die nachfolgenden _nicht_ mehr ausgeführt, das Programm bleibt jedoch am Leben. Und was wäre jetzt einfacher, als _nur_noch_einmal_ abfragen zu müssen, ob eine Ex. geworfen wurde?

                        Zu Drittanbindungen: du hast explizit Datenbank-Exceptions

                        Nein. Ich meinte auch in meinem letzten Post nur den Layer.

                        Hotti

                        --
                        Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
                        1. Hi,

                          Die Frage war, in welchen Fällen Exceptions durch die Datenbank geworfen werden.
                          Exceptions wirft der Layer, nicht die DB

                          Entschuldige, aber das ist Wortklauberei. Mir ist klar, dass die DB keine Exceptions werfen kann. Wenn ich von DB spreche, dann meine ich die DB-Verbindungsbibliothek (den "Layer").

                          Freilich kannst Du nach jedem einzelnen Statement auch ohne eine Exception auszulösen, prüfen, ob ein Fehler aufgetreten ist.

                          Du hast meinen Punkt nicht verstanden. Ich bin darauf eingegangen, welche Arten von Datenbank-Fehlern es gibt. Ich habe kein Problem mit Exceptions durch die Datenbank (bzw. den Layer, wenn du das lieber hören willst), ganz im Gegenteil: ich lasse mir auch Exceptions werfen, wenn die Datenbank eine Query nicht erfolgreich beenden konnte.

                          In zwei von drei von mir aufgezählten Fällen ist es aber keine Geschäftslogik, die zugrunde liegen. Es wird keine Geschäftslogik geben, wenn ich einen Bug in meine Queries reinbaue. Es wird keine oder nur wenig Geschäftslogik geben, wenn die Datenbank nicht erreichbar ist.
                          Der Ausnahmefall ist Fall (c): nämlich dann, wenn ich auf Verdacht eine Query absetze und die Datenbank mir dann mitteilt, ob es erfolgreich war. Als Beispiel habe ich einen UNIQUE-Constraint genannt, wird ja häufig gerne für fachliche Schlüssel eingesetzt sowas.
                          Beispiel (mal ganz plastisch): wir sind in der Kennzeichenzentrale. In der Kennzeichenspalte ist  ein UNIQUE-constraint.
                          Ich versuche ein Autokennzeichen in die Datenbank einzutragen.
                          Möglichkeit (i): Ich locke die Tabelle, schaue nach, ob das Kennzeichen existiert. Wenn nein, dann schreibe ich das Kennzeichen rein. Wenn es existiert, dann behandle den Fall ("Kennzeichen existiert"). Entferne das Lock.
                          Möglichkeit (ii): Ich schreibe das Kennzeichen rein. Wenn die Datenbank einen Constraint-Fehler meldet, behandle ich den Fall ("Kennzeichen existiert").

                          _Das_ ist Geschäftslogik, und wenn du die DB (den DB-Layer) dazu bringst, aus "Nicht-erfolgreichen Queries" Exceptions zu machen, dann musst du Geschäftslogik mit Exceptions abhandeln.

                          Und noch ein Unterschied zwischen Fall (a)+(b) und (c):
                          Die Exceptions in den Fällen (a) und (b) wirst du doch eher global abarbeiten. D.h. "um dein ganzes Programm rum" wirst du ein try/catch-setzen, welches die Exception fängt und dann entsprechend dein Programm sauber herunterfährt oder sowas.
                          Im Fall (c) ist deine Exception-Verarbeitung aber sehr lokal. Du willst dann nämlich genau für ein Query wissen, ob es erfolgreich war. Aber dann hast du keinerlei Vorteil gegenüber einer einfachen Überprüfung auf den Rückgabewert des Queries, der Aufwand ist gleich hoch.

                          Bitte zeige mir noch mehr Fälle, in denen ein DB-Query nicht erfolgreich ist und nicht Fall (a) bis (c) eintritt. Und dann kann man auch (im Beispiel Exceptions durch DB) entscheiden, ob es sinnvoll oder nicht sinnvoll ist, Geschäftslogik durch Exceptions zu behandeln.

                          Bis die Tage,
                          Matti

                          1. hi,

                            Die Exceptions in den Fällen (a) und (b) wirst du doch eher global abarbeiten. D.h. "um dein ganzes Programm rum" wirst du ein try/catch-setzen, welches die Exception fängt und dann entsprechend dein Programm sauber herunterfährt oder sowas.

                            Neiiiiin. Nicht global ;)

                            Meine DB-Zugriffe organisiere ich in Klassen, wobei ich das Ex.Model nur innerhalb der Klasse anwende, Beispiel:

                              
                            # Lege Artikel in den Warenkorb  
                            sub pick{  
                              my $self = shift; # Response-Klasse  
                              my $cart = Cart->new(%{$self->{CFG}{mysql}}) or do{  
                                $self->errpush("Bedaure, keine Verbindung zur DB", $@);  
                                return;  
                              }  
                              $cart->pick(%article) or do{  
                                $self->errpush("Leider schiefgegangen", $@);  
                                return;  
                              };  
                              # alles OK, weiterer code  
                            }  
                            
                            

                            Zum Debuggen gebe ich außerdem $@ aus, da steht der Grund der Ex. drin, die innerhalb der Klasse Cart aufgetreten ist. Das interessiert den Kunden zwar weniger, ist jedoch für eine Ferndiagnose hilfreich ;)

                            Betrachte jedoch mal folgenden Hack (6 Zeilen Code gespart):

                              
                            sub pick{  
                              my $self = shift;  
                              my $cart = Cart->new(%{$self->{CFG}{mysql}}) or die $@;  
                              $cart->pick(%article) or die $@;  
                              # alles OK, weiterer code zur Erfolgsmeldung...  
                            }  
                            
                            

                            Unverschämt! Würde ein Fremder sagen, der Kunde bekommt einen 500er, die Anwendung stirbt ja weg!

                            Nein, macht sie nicht ;)
                            Der Code der pick()-Methode liegt in dem Moment, wo die Methode aufgerufen wird, als anonyme Code-Referenz im Haupspeicher. Aber nicht nur das: Mein Programm hat den Zugriff über $code. Wie geil: Hier wird die Ex. abgefangen.

                            Im Fall (c) ist deine Exception-Verarbeitung aber sehr lokal. Du willst dann nämlich genau für ein Query wissen, ob es erfolgreich war. Aber dann hast du keinerlei Vorteil gegenüber einer einfachen Überprüfung auf den Rückgabewert des Queries, der Aufwand ist gleich hoch.

                            Ok, zurück zu DB-Zeugs. Wie gesagt, ich organisiere in Klassen, für alles, was zu tun ist, gibts Methoden, die sehen in der letzten Zeile alle gleich aus:

                              
                               return $@ ? undef : $rv;  
                            
                            

                            Dabei ists egal, ob im try-Block ein oder mehrere Statements notiert sind, wenn bei einem der ST ein Fehler auftritt, steht der Grund in $@, fertig.

                            Bitte zeige mir noch mehr Fälle, in denen ein DB-Query nicht erfolgreich ist und nicht Fall (a) bis (c) eintritt. Und dann kann man auch (im Beispiel Exceptions durch DB) entscheiden, ob es sinnvoll oder nicht sinnvoll ist, Geschäftslogik durch Exceptions zu behandeln.

                            Es ist keine Schande, Programmlogik per Ex. zu entscheiden, es gibt, richtig organisiert, erhebliche Vereinfachungen gegenüber herkömmlichen Kontrollstrukturen. Es muss nur dafür gesorgt werden, dass bis zum Schluss ein stabiler Zustand besteht. Dem Anwender schließlich ists auch egal, mit welchen Mitteln die Anwendung intern arbeitet.

                            Wenn ich mir ein Modul von CPAN schnappe und weiß, dass ich Fehler in $@ finde, weiß ich auch, dass Modul-Intern mit Exceptions gearbeitet wird, anderweitig würde nichts in $@ stehen. Abstrakt gesehen, ists mir als Anwender eines CPAN-Moduls jedoch egal, wie das intern arbeitet, hauptsache, ich weiß, wo ich die Fehlermeldungen finde.

                            Hotti

                            --
                            Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
                    2. Tach,

                      Anderer Fall, wo Exceptions hilfreich sind: Die Tage habe ich ein Modul geschrieben für Kalenderbechnungen. Zu möglichen Eingabefehlern (Datum) habe ich nicht weniger als 37 verschiedene Fehlermeldungen, die innerhalb des Moduls an sehr verschiedenen Stellen auftreten können.

                      ich würde behaupten, du machst etwas falsch. Mir fallen spontan drei (vier) Dinge ein, die an einem übergebenen Datum falsch sein können: der übergebene Wert ist kein Datum in parsebarer Form , (parsebar aber das Ergebnis ist unerwartet z.B. 2011-02-29), das Datum ist zu früh oder zu spät. Welche weiteren 34 Fälle erzeugst du noch?

                      mfg
                      Woodfighter

                      1. hi,

                        ich würde behaupten, du machst etwas falsch. Mir fallen spontan drei (vier) Dinge ein, die an einem übergebenen Datum falsch sein können: der übergebene Wert ist kein Datum in parsebarer Form , (parsebar aber das Ergebnis ist unerwartet z.B. 2011-02-29), das Datum ist zu früh oder zu spät. Welche weiteren 34 Fälle erzeugst du noch?

                        Der Benutzer darf ein Datum eingeben. Zu
                        G- Gregorianischen Kalender
                        J- Julianischen Kalender
                        B- beliebig (wird im Programm entschieden, zu welcher Epoche J|G es gehört)
                        JD- Weitere Eingabemöglichkeit ist der Julianische Tag, der muss positiv, numerisch, integer sein.

                        Mögliche Fehler
                        JD Nicht numerisch                                                        1
                        G Format, kein Schaltjahr, ungültig                                       3
                        J Format, kein Schaltjahr, ungültig                                       3
                        B Format, kein Schaltjahr, ungültig, ungültig aufgrund Greg. Reform       4
                        G Jahr 0, Mon 0, Tag 0                                                    3
                        J Jahr 0, Mon 0, Tag 0                                                    3
                        B Jahr 0, Mon 0, Tag 0                                                    3
                                                                                              ------
                                                                                                 20
                        Die Prüfungen für B sind jedoch an 2 weiteren Stellen (Fall J, G)         6
                        für G, J, B dazu
                        Mon nicht angegeben, Tag nicht angegeben, Jahr nicht angegeben            9

                        Ungültig heißt z.B. Monat > 12, Tag > 31 o.ä.

                        Macht zusammen 35 ;)

                        Dazu kommen Exceptions, die beim falschen Anwenden des Moduls auftreten, die werden mit Carp erzeugt und zeigen dem Programmierer ein Backtrace, z.B. wenn die übergebene Argumentenliste nicht passt.

                        Es sind zwar Überschneidungen, aber im Modul sind die Prüfungen verteilt auf kurze und schmerzlose die "...\n".

                        Was dabei rauskommt, ist ein Objekt und: Wie Du in einem anderen Post richtig bemerkt hast, ists eine Exception, wenn das Objekt nicht erstellt werden konnte ;)

                        Das Modul hat überschaubare 277 Zeilen, wenn ich die Prüfungen über normale Kontrollstrukturen machen würde, wären weit über 1000 Zeilen Code entstanden.

                        Hotti

                        1. Das Modul hat überschaubare 277 Zeilen, wenn ich die Prüfungen über normale Kontrollstrukturen machen würde, wären weit über 1000 Zeilen Code entstanden.

                          Häh?! Ein paar mehr Zeichen könnten es werden, aber keine Zeilen.

                          obj->selfdestruct() || die 'dead';
                          vs
                          obj->selfdestruct() || return obj->errordings('dead');

                          1. Das Modul hat überschaubare 277 Zeilen, wenn ich die Prüfungen über normale Kontrollstrukturen machen würde, wären weit über 1000 Zeilen Code entstanden.

                            Häh?! Ein paar mehr Zeichen könnten es werden, aber keine Zeilen.

                            obj->selfdestruct() || die 'dead';

                            Hah! Sag ich doch die ganze Zeit ;)

                        2. Tach,

                          ich sagte ja, du machst etwas falsch: Ohne den Quelltext deines Programms gesehen zu haben, ist nach deinen Ausführungen offensichtlich, dass du Code erzeugt hast, der nicht gut sein kann. Du testest Eingabedaten an verschiedenen Stellen auf die selben Fehler, nach deinen Ausführungen ohne dabei Funktionen oder ähnliches zu nutzen. Sinnvoller, guter oder effektiver Code sieht anders aus.

                          Was dabei rauskommt, ist ein Objekt und: Wie Du in einem anderen Post richtig bemerkt hast, ists eine Exception, wenn das Objekt nicht erstellt werden konnte ;)

                          Ich sehe zwar den Smiley, gewinne aber das Gefühl, dass du nicht dazu in der Lage bist das Konzept zu erfassen.

                          Das Modul hat überschaubare 277 Zeilen, wenn ich die Prüfungen über normale Kontrollstrukturen machen würde, wären weit über 1000 Zeilen Code entstanden.

                          Dass das Unsinn ist, hat ja schon Mitleser aufgezeigt.

                          mfg
                          Woodfighter

                3. Tach,

                  Darüber könnten wir ewig streiten ;)

                  nein, darüber brauchen wir nicht streiten, das ist bereits geklärtes Wissen.

                  Fakt ist jedoch: Zur Fehlerbehandlung in Perl-DBI wird RaiseError nach wie vor empfohlen und genau das läuft auf eine Exception.

                  Darauf ist Matti ja schon eingegangen. Hat allerdings auch nicht das geringste mit deinem Beispiel zu tun, dort hast du Dinge aufgeführt, die eindeutig keine Exceptions auslösen sollten.

                  Aufwändiger Prozess? Nein, im Gegenteil: Aufwändige Kontrollstrukturen entfallen, der Code wird übersichtlicher!

                  Was hat das mit dem Prozess, der durch eine Exception im Hintergrund ausgelöst wird zu tun? Kontrollstrukturen sind weniger aufwendig abzuarbeiten als Exceptions.

                  mfg
                  Woodfighter

                  1. hi,

                    [..] Kontrollstrukturen sind weniger aufwendig abzuarbeiten als Exceptions.

                    Ja: Wenn Du in jedem if/else Zweig ein try/catch einbauen würdest ;)
                    Nein: Wenn try/catch nur einmal erfolgt, nämlich an einer dafür geeigneten Stelle, z.B. vor der Ausgabe auf STDOUT oder der Rückgabe eines Objects aus dem Konstruktor heraus.

                    Zu Letzterem: Es gibt auf CPAN ungezählte Perl-Module, deren Synopsis so aussieht:

                      
                    my $obj = Class->new or print $@;  
                    
                    

                    Ich habe bewusst "print" geschrieben, aber was spielt sich denn ab, wenn das Objekt nicht erstellt werden konnte???? Richtig: Innerhalb Class wurde eine Exception geworfen, deren ErrCode in $@ zu finden ist!

                    Ausnahmen bestägigen die Regel ;)

                    1. Tach,

                      [..] Kontrollstrukturen sind weniger aufwendig abzuarbeiten als Exceptions.

                      Ja: Wenn Du in jedem if/else Zweig ein try/catch einbauen würdest ;)
                      Nein: Wenn try/catch nur einmal erfolgt, nämlich an einer dafür geeigneten Stelle, z.B. vor der Ausgabe auf STDOUT oder der Rückgabe eines Objects aus dem Konstruktor heraus.

                      nein, siehe z.B. https://blogs.msdn.com/b/ricom/archive/2006/09/25/771142.aspx?Redirected=true

                      Ich habe bewusst "print" geschrieben, aber was spielt sich denn ab, wenn das Objekt nicht erstellt werden konnte???? Richtig: Innerhalb Class wurde eine Exception geworfen, deren ErrCode in $@ zu finden ist!

                      Das hat nichts mit Geschäftslogik zu tun. Wenn ein Objekt nicht erstellt werden kann, ist das logischerweise eine Exception wert; wie ich schon zitierte: „breaching of predefined assumption of the application“, in diesem Falle vermutlich etwas wie fehlende Bibliotheken, zu wenig Speicher etc.

                      mfg
                      Woodfighter

            2. Warum auch nicht das hier:
              [$SIG{__WARN__}-Handler]
              Weil's Murks ist.
              Nein im Gegenteil.

              Nein, im Gegenteil.

              Ich hatte versäumt, explizit zu sagen, warum es Murks ist: es ist ein globales Konstrukt. Ich dachte, du wärst so schlau, mit dem aufwärts angegebenen Link zu dieser Folgerung zu kommen.

              Ein [$SIG{__WARN__}-Handler]
              mag zwar etwas altmodisch erscheinen

              Veraltet ist das richtige Wort. Genauso wie globale Filehandles benutzt man es nicht mehr, weil es moderne lexikalische Entsprechungen gibt.

              vorausgesetzt, Du kannst mit Exceptions richtig umgehen.

              [Erläuterung von Exceptions entsorgt.]
              Das war nicht der Punkt. Du weißt sehr wohl, dass ich Exceptions beherrsche.
              </archiv/2012/1/t208670/#m1419506>
              </archiv/2010/1/t194462/#m1302321>

              Was glaubst Du wohl, wer sich mit 'Man' angesprochen fühlt!?

              Die Leser, wer denn sonst?

          2. Moin CPAN,

            Kann das Forum nicht mal fachlich unhilfreich einführen?

            Gabs sogar mal. Technisch kann das Forum das.

            LG,
             CK

  3. ich habe ein Hash-Array wie folgt:

    Ich verstehe Hash of Arraysrefs
    also
    $hash = (a=>[0,1,2,3,4],b=>[]);

    Du jedoch hast einen HashOfHashesOfHashesOfHashesOfHashesOfHashes der Tiefe 4 notiert.

    my %ip;
    $ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}+=$bytes_total;

    ...

    my %sorted = sort {$a->[4] <=> $b->[4]} %ip;

    Du greifst hier auf
    $hash{id}[4] also auf das fünfte Element eines Hash of Arrays zu.
    Stelle sicher, dass deine Datenstruktur auch ein HashOfArrays ist.

    mfg Beat

    --
    ><o(((°>           ><o(((°>
       <°)))o><                     ><o(((°>o
    Der Valigator leibt diese Fische
    1. ich habe ein Hash-Array wie folgt:

      Ich verstehe Hash of Arraysrefs
      also
      $hash = (a=>[0,1,2,3,4],b=>[]);

      Du jedoch hast einen HashOfHashesOfHashesOfHashesOfHashesOfHashes der Tiefe 4 notiert.

      my %ip;
      $ip{"source"}{$src_ip}{$src_port}{$dest_ip}{$dest_port}+=$bytes_total;
      ...
      my %sorted = sort {$a->[4] <=> $b->[4]} %ip;

      Du greifst hier auf
      $hash{id}[4] also auf das fünfte Element eines Hash of Arrays zu.
      Stelle sicher, dass deine Datenstruktur auch ein HashOfArrays ist.

      mfg Beat

      Ok, das ist aber von mir so gewollt, dass es ein mehrdimensionales Hash ist. Wie kann man also in einem mehrdimensionalen Hash nach dem Wert von $dest_port sortieren? Oder die sortierte Reihenfolge der Keys ausgeben?