(C)2009, Kiermaier Variablen - %Hashes Was ist ein Hash? 'Assoziative Arrays' Hashes sind array-ähnliche Datenstrukturen, die aus Schlüssel-Wert Paaren bestehen. Artikel + Preis Apfel Banane Ananas Birne %Artikel = Apfel 0,45 0,75 1,49 0,59 0,45 Banane 0,75 Ananas 1,49 Birne 0,59 Schlüssel Skalarer Wert (key) (value) 64 Vom Prinzip her sind sich ein Array und ein Hash relativ ähnlich. Bei beiden handelt es sich um eine Gruppe von Daten, bei der einzelne skalare Elemente über einen Index ausgewählt werden. Allerdings sind bei Hashes diese Indizes in der Regel keine Zahlenwerte, sondern statt dessen beliebige Strings bzw. Skalare. Diese Strings bzw. Skalare werden Schlüssel oder Keys genannt. Die Elemente (Values) eines Hashes sind wegen der fehlenden Indizes auch nicht besonders geordnet, wie bei Arrays üblich. Im Gegenteil ist die Reihenfolge der einzelnen Werte sogar völlig unerheblich, weil sie auf Grund der Schlüssel leicht gefunden werden können. Wenn man neue Werte hinzufügt, wird einfach ein neuer Hashwert angehängt. Die Perl-interne Suche nach einem dazugehörigen Wert ist über einen Schlüsselstring natürlich langsamer als über einen numerischen Index. 64 (C)2009, Kiermaier Variablen - %Hashes Wie erzeuge ich einen Hash? Wie eine Liste (assoziative Liste): %Artikel = ("Apfel", 0.45, "Banane", 0.75, "Ananas", 1.49, "Birne", 0.59); Mit dem Doppelpfeil-Operator (comma arrow): %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); oder als einzelne skalare Werte: erzeugt in anderer Reihenfolge den Hash: $Artikel{'Apfel'} = 0.45; $Artikel{'Banane'} = 0.75; $Artikel{'Ananas'} = 1.49; %Artikel = $Preis = 0.59; $Artikel{'Birne'} = $Preis; Ananas 1,49 Banane 0,75 Apfel 0,45 Birne 0,59 65 Prinzipiell kann ein assoziatives Array (also ein Hash) genauso erzeugt werden, wie eine 'normales' Array. Also schreibt man einfach, durch Kommas getrennt, abwechselnd Schlüssel und dazugehörige Werte in eine Liste. Deutlicher ist vielleicht die Schreibweise mit dem Pfeil-Operator. Dabei stehen zwischen dem Schlüssel und dem dazugehörigen Wert die Zeichen '=>'. Während man bei Arrays Barewords vermeiden sollte, sind hier die Anführungsstriche nicht in jedem Falle nötig. Durch den Pfeil-Operator wird der Schlüssel ohnehin nicht interpretiert oder berechnet. Da auch ein Hash aus einzelnen skalaren Datenwerten besteht, kann man ihn auch in der Skalar-Schreibweise mit dem Schlüssel in geschweiften Klammern definieren. Beachten Sie dabei, dass die Schlüssel dabei in Hochkommas stehen (Barewords vermeiden!)! In allen Fällen ist zu beachten, dass die Reihenfolge, in der Sie einen Hash definieren, nicht von Perl eingehalten wird! Die Schlüssel dürfen selbstverständlich nicht nur aus Zeichenketten sondern auch aus Zahlen bestehen, was zum Teil verwirrend einem Array gleichen kann: %PLZ = (0, 84036, 1, 84028, 2, 84032); Dies ergibt einen Hash, der als 'Index' die Zahlen 0 bis 2 hat. Gespeichert wird jedoch: 1=>84029, 0=>84036, 2=>84032. Um einen Hash mit Variablenzwang (use strict) zu deklarieren, schreiben Sie einfach my %hash = (); Also wie bei einem Array wird zum Deklarieren einfach eine leere Liste zugewiesen. 65 (C)2009, Kiermaier Variablen - %Hashes Wie benutzt man einen Hash? %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); print %Artikel, "\n"; Interpolation geht nicht! print "%Artikel\n"; Statt dessen Zuweisung in ein Array und dessen Ausgabe mit Trennzeichen: $" = ", "; @rray = %Artikel; print "@rray\n"; Einzelne Werte ausgeben (auch mit Interpolation): print $Artikel{'Apfel'},"\n"; print "$Artikel{'Birne'}\n"; 66 Um einen Hash auszugeben, sollte man beachten, dass ein Hash in Anführungszeichen nicht interpoliert wird. Mit der Print-Anweisung erscheint ein Hash genauso wie ein Array. Will man die einzelnen Elemente mit Trennzeichen ansehen, ist es praktisch, sie in ein Array zu übertragen und dann dieses auszugeben. Einzelne Hashwerte hingegen werden auch in Anführungsstrichen interpoliert und ausgegeben. Übung: Schreiben Sie ein Programm, dass eine oder mehrere Textzeilen einliest und mit einer Leerzeile beendet wird. Zerlegen Sie den Text in einzelne Worte und zählen dann, wie oft jedes Wort vorkam. Benutzen Sie dazu einen Hash und die Worte als Schlüssel. Geben Sie am Schluss den Hash aus. Lösung: 66 (C)2009, Kiermaier Variablen - %Hashes Wie verändert man einen Hash? Einen einzelnen Hash-Eintrag löschen: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); delete $Artikel{'Ananas'}; print %Artikel, "\n"; Mehrere Hash-Einträge (Slices) löschen: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); delete @Artikel{'Ananas', 'Banane'}; print %Artikel, "\n"; 67 Ein einzelner Hash-Eintrag wird einfach über seinen Schlüssel gelöscht. Da es ein einzelner Eintrag ist verwendet man das Skalarzeichen (= Einzelwert) und die geschweifte Klammer mit dem Schlüssel, welche es als Hash kennzeichnet. Mehrere Hashes sind ja nichts anderes als eine Liste. Deshalb werden Slices so gelöscht, dass man das Arrayzeichen (= Werteliste) verwendet. Dahinter steht in geschweiften Klammern eine Liste der Schlüssel, deren Hash-Eintrag gelöscht werden soll. Wollen Sie gleich den ganzen Hash löschen, weisen Sie ihm einfach wieder eine leere Liste zu: %Hash = ( ); 67 (C)2009, Kiermaier Variablen - %Hashes Hashfunktionen: mit Keys die Schlüssel finden @Liste = keys(%Hash) Beispiel: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); @Liste = keys(%Artikel); print "@liste\n"; Beispiel mit sortierter Liste: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); @Liste = sort (keys(%Artikel)); print "@liste\n"; 68 Die Funktion keys(%hash) erzeugt eine Liste aller Schlüssel im Hash. Anders ausgedrückt liefert diese Funktion alle ungeraden Elemente (das erste, das dritte, fünfte, etc.) der Hash-Liste zurück. Da man in der Regel nicht weiß, in welcher Reihenfolge die Hash-Elemente gespeichert sind, kann man mit Sort die Schlüssel sortieren. Im skalaren Kontext liefert keys die Anzahl der Elemente im Hash zurück, z.B. $n = keys(%hash). Übung: Schreiben Sie ein Programm, dass einzelne Wörter einliest bis man Strg+Z drückt (Einlesen je Zeile in ein Array). Dann zählt es, wie oft jedes Wort vorkam. Geben Sie die alphabetisch sortierten eingegebenen Wörter und die Häufigkeit nebeneinander aus. Lösung: 68 (C)2009, Kiermaier Variablen - %Hashes Hashfunktionen: mit Values die Werte finden @Liste = values(%Hash) Beispiel: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); @Liste = values(%Artikel); print "@liste\n"; Beispiel mit numerisch sortierter Liste: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); @Liste = sort {$a <=> $b} (values(%Artikel)); print "@liste\n"; 69 Das Gegenteil zu keys ist die Funktion values. Sie liefert die Hash-Werte zurück, also alle geraden Elemente einer Liste. Übung: Schreiben Sie das vorherige Programmm (zur keys-Funktion) so um, dass eine Rangfolge der Wort-Häufigkeit entsteht. Also das häufigste Wort an erster Stelle und das seltenste an letzter Stelle. Lösung: 69 (C)2009, Kiermaier Kontrollstrukturen Endlosschleifen mit While und Until Schleife ausführen, solange (Vergleich) zutrifft, also wahr ist: while (Vergleich) {Anweisungsblock} Schleife ausführen, solange (Vergleich) nicht zutrifft, also falsch ist: until (Vergleich) {Anweisungsblock} Beispiel zu while: print "Wie alt bist du?"; chomp($alt = <stdin>); while ($alt > 0) { print "Du warst mal $alt Jahre alt.\n"; $alt--; } Beispiel zu until: print "Wie alt bist du?"; chomp($alt = <stdin>); until ($alt == 0) { print "Du warst mal $alt Jahre alt.\n"; $alt--; } 70 Neben den Schleifenkonstruktionen mit For und ForEach gibt es in Perl auch andere Konstruktionen, die praktisch anwendbar sind. Während For meist mit einer Schleifenvariable eine begrenzte Anzahl von Durchläufen benutzt, werden die Anweisungen While und Until für Endlosschleifen benutzt. Die Abbruchbedingung ergibt sich aus einem Vergleich, der wahr oder falsch sein kann. Die While-Schleife wird solange durchlaufen, solange ein Vergleich den Wert wahr (=1) ergibt. Die Anweisung while (1) ergibt also eine Endlosschleife, die nur mit Last wieder verlassen werden kann (die Bedingung ist immer erfüllt). Die Until-Schleife reagiert genau anders herum: sie wird durchlaufen, solange ein Vergleich den Wert falsch ergibt. Also solange, bis der Vergleich wahr wird. Demzufolge stellt die Anweisung until (0) ebenso eine Endlosschleife. Gerne benutzt wird auch die folgende Anweisung Do { Anweisungsblock } while (Vergleich); Das ist immer dann interessant, wenn der Anweisungsblock wenigstens einmal durchlaufen werden soll. While bzw. Until statt do würde statt dessen den Anweisungsblock sofort überspringen, wenn der Vergleich erfüllt ist. 70 (C)2009, Kiermaier Variablen - %Hashes Hashfunktionen: mit each Schlüssel/Wertpaare finden @Eintrag = each(%Hash) ($Schlüssel, $Wert) = each(%Hash) Beispiel: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); foreach (keys (%Artikel)) { ($key, $Wert) = each (%Artikel); print "$key\t $Wert\n"; } Beispiel mit while-Schleife: %Artikel = (Apfel => 0.45, Banane => 0.75, Ananas => 1.49, Birne => 0.59); while (($key, $Wert) = each(%Artikel)) { print "$key\t $Wert\n"; } 71 Mit der Funktion 'each' werden nacheinander alle Schlüssel/Wertpaare einer Liste zugewiesen. Die Each-Funktion liefert im skalaren Kontext eine 0 zurück, wenn keine Schlüssel/Wertpaare mehr vorhanden sind. Deshalb eignet sich diese Funktion besonders als Abbruchbedingung für While-Schleifen. 71 (C)2009, Kiermaier Variablen - %Hashes Mit Reverse die Schlüssel/Wertpaare vertauschen %NeuHash = reverse (%Hash) Beispiel: %Internet = (Deutschland => de, Oesterreich => at, Schweiz => ch); while (($land, $kurz) = each(%Internet)) { print "$land hat Webkuerzel $kurz\n"; } %Internet = reverse (%Internet); #Schlüssel und Wert vertauschen while (($kurz, $land) = each(%Internet)) { print "Webkuerzel $kurz gehoert zu $land\n"; } 72 Mit der Reverse-Funktion lassen sich auch die Schlüssel/Wertpaare vertauschen. Damit ist es möglich in einem Hash nach dem Wert oder nach dem Schlüssel zu sortieren und zu suchen. Zu beachten ist dabei allerdings, dass die Zuordnung eindeutig sein muss. Das heißt, wenn Werte doppelt vorkommen, sind anschließend die ersten Einträge des Werts gelöscht! Übung: Schreiben Sie ein Programm, das als Überschrift ***Telefonliste anlegen*** ausgibt. Dann werden solange Namen und anschließend Telefonnummern eingelesen, bis kein Name mehr eingegeben wird. Die Paare werden in einem Hash gespeichert. Nach zwei Leerzeilen steht die Überschrift 'Freundeliste'. Dann werden sortiert die Namen und nach drei Tabulatoren die dazugehörigen Telefonummern ausgegeben. Nach zwei weiteren Leerzeilen wird dann die Überschrift 'Telefonliste' und sortiert die Telefonummern und nach drei Tabulatoren die dazugehörigen Namen ausgegeben. Lösung: 72 (C)2009, Kiermaier Variablen - %Hashes Hash von Hashes %Hash = ( %Hash1, %Hash2, …) Beispiel: %Tabelle = ( 'Zeile1' => {'U' => 10, 'I' => 0.01, 'R' => 0}, 'Zeile2' => {'U' => 10, 'I' => 0.09, 'R' => 0}); foreach $zeile (keys %Tabelle) { #zeilenweise auslesen print "$zeile\n"; $u = $Tabelle{$zeile}{'U'}; #Element U aus $zeile $i = $Tabelle{$zeile}{'I'}; #Element I aus $zeile $Tabelle{$zeile}{'R'} = $Tabelle{$zeile}{'U'} / $Tabelle{$zeile}{'I'}; print "$u / $i = $Tabelle{$zeile}{'R'}\n"; foreach $key (keys %{$Tabelle{$zeile}}) { #alle Elemente der Zeile print "$key = $Tabelle{$zeile}{$key}; "; #mit dem Schlüssel ausgeben } print "\n"; } 73 Wie schon bei den Arrays kann man auch Hashes verschachteln. Es gibt deshalb auch Arrays von Hashes, Hashes von Arrays und Hashes von Hashes. Während man bei zweidimensionalen Arrays auf ein Element mit den Indizes in eckigen Klammern zugegriffen hat, erfolgt es beim Hash von Hashes über die geschweiften Klammern. Bei den Mischformen sind dann auch die Klammern gemischt (Es geht beliebig kompliziert, was man auch einfach machen könnte. Siehe dazu besser andere Programmiersprachen). 73 (C)2009, Kiermaier Variablen – Besondere Variablen Systemvariablen Der Hash für die Umgebungsvariablen heißt %ENV. Beispiel (gibt die Windows-Umgebungsvariablen sortiert aus): while (($var, $wert) = each(%ENV)) { printf "%-24s = %-s\n", $var, $wert; } Beispiel (gibt Ihren Anmeldenamen und Computernamen aus): print "Sie sind User $ENV{USERNAME}.\n"; print "Sie sitzen an dem $ENV{'OS'}-Rechner $ENV{'COMPUTERNAME'}.\n" Das Array für die Aufrufparameter beim Start des Programms heißt @ARGV. Beispiel (Aufruf z.B. mit perl addiere.pl 3 4 5): if ($#ARGV == -1) {die "Aufruf mit Parameter \'zahl1 zahl2\'\n"} foreach (@ARGV) {$erg += $_} print "Summe = $erg\n"; 74 Es gibt eine Menge Sondervariablen die bestimmte Inhalte präsentieren. Die Umgebungsvariablen sind zum Beispiel in dem Hash %ENV gespeichert. Dort können Sie mit $ENV{Schlüssel} abgefragt oder auch verändert werden, allerdings nicht systemweit und dauerhaft. Wenn man hinter einem Programm weitere Angaben setzt (=Aufrufparameter) kann man diese im Array @ARGV ansehen. Wenn das Array leer ist, also nichts übergeben wurde, steht der höchste Index auf -1. Oft wird ein Aufrufparameter auch mit shift (ohne weitere Argumente) abgeholt. Dann ist die Benutzung von @ARGV nicht nötig (aber leider etwas undurchsichtig, woher der Wert kommt). Das sieht dann so aus: $werte = shift; @liste = split(/ /, $werte); Wenn $werte leer ist, wurden keine Parameter übergeben. 74 (C)2009, Kiermaier Variablen – Besondere Variablen Systemzeit in ein Array lesen @Datumzeit = localtime(time) Beispiel: use strict 'vars'; my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat, $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time); my %zeit = (Std => $Stunden, Min => $Minuten, Sek => $Sekunden); my %datum = (Tag => $Monatstag, Mon => $Monat+1, Jhr => $Jahr+1900); my @Wochentage = ("Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"); #Formatierung: %02u => 0 mit Null auffüllen, 2 Stellen, u unsigned Integer printf "Aktuelle Uhrzeit ist %02u:%02u:%02u.\n", $zeit{Std}, $zeit{Min}, $zeit{Sek}; printf "Heute ist $Wochentage[$Wochentag], der %02u.%02u.%04u\n", $datum{Tag}, $datum{Mon}, $datum{Jhr}; 75 Die Funktion localtime(time) ermittelt eine Liste mit den aktuellen Zeitwerten des Computers. Manche dieser Werte müssen noch etwas angepasst werden, weil die Rückgabewerte mit 0 statt mit 1 beginnen. Dazu gehört der Monat und der Jahrestag (der wievielte Tag des Jahres). Außerdem beginnen die Jahre erst bei 1900 mit dem Wert 0. Man muss also 1900 zur Jahreszahl addieren. 75