Liste oder Hash? - Theorie Lektion 6

Erstellt von Frithjof Sun, 02 Sep 2007 00:07:00 GMT

Die Ferien neigen sich dem Ende entgegegen und der letzte Artikel liegt auch schon etwas länger zurück. Es wird also Zeit für die letzte Theorielektion, bevor wir uns demnächst mit der Programmierung eines Spieles befassen werden.

Ich möchte noch einmal auf Listen und Hashes zu sprechen kommen, die wir bereits in der letzten Theorielektion 5 kennen gelernt hatten. Schauen wir uns hier einmal genauer an, wann es sinnvoll ist mit Listen zu arbeiten und wann ein Hash zu bevorzugen ist. Wir werden dazu folgendes untersuchen:

  1. Wie greift man auf die Elemente in einer Liste oder einem Hash zu?
  2. Wie stellt man fest, ob ein bestimmtes Element in der Liste enthalten ist oder nicht?
  3. Wie fügt man Element in eine Liste oder Hash hinzu oder löscht welche heraus?

Bildlich kannst du dir eine Liste wie eine Kette vorstellen, an der verschiedene Dinge aufgereiht werden. Die Liste


  liste = ["Auto", "Haus", "Baum", "Ball", "Buch", "Bett", "PC"]

würde dann etwa so aussehen:

Einen Hash stellst du dir hingegen vor wie die Kleiderhaken in deinem Kindergarten. Jeder Haken ist mit einem Bildchen versehen. Alle Kinder im Kindergarten bekommen je einen Kleiderhaken zugeordnet und erkennen ihren eigenen leicht an seinem Bildchen. Die folgenden Kleiderhaken

könntest du dann in Ruby vielleicht so ausdrücken:


kleiderhaken = {
  "Ball"   => "Susi",
  "Baum"   => "Livia",
  "Haus"   => "Petra",
  "Schiff" => "Rainer",
  "Auto"   => "Peter",
}

Jetzt, wo du dir beide Datenstrukturen ein wenig bildlich vorstellen kannst, zeige ich dir ein paar der wichtigsten Operationen. Eine Operation soll hier jede Veränderung, die man an oder mit der Datenstruktur vornimmt, bedeuten.

Zugriff auf Elemente

Angenommen wir haben folgende Liste und Hash:

kette = ["Auto", "Haus", "Baum"]
haken = {
  "Auto" => "Peter",
  "Haus" => "Petra",
  "Baum" => "Livia",
}

Wir wollen das zweite Element in der Liste haben. An dieser Stelle musst du dich in die Lage des Rubyinterpreters versetzen. Anders als du als Entwickler sieht er nicht, dass das zweite Element in der Liste “Haus” ist. Daher ist die Frage nach dem zweiten Element für dich zwar baby-einfach, aber für den Rubyinterpreter nicht. Ihm musst du genau sagen, an welcher Stelle in der Liste mit Namen kette er das Element zugreifen soll. Die Zahl, die du Ruby dafür angibst nennt man auch den Index der Liste. Tun wir das:


puts "Das zweite Element der Liste ist " + kette[1]

In Listen werden die Elemente ja beginnend mit 0 (Null) abgezählt. Daher ist das zweite Element eben nicht beim Index 2, sondern 1 abgelegt. Unterscheide also zwischen dem Index und der Position innerhalb der Liste. Der Index ist immer um Eins kleiner, als die Position an der das Element in der Liste steht.

Nun willst du auch im Hash mit dem Namen haken an das zweite Element heran. Schauen wir, was passiert, wenn wir das genauso machen:


puts "Das zweite Element im Hash ist " + haken[1]

Naja, so geht es wohl nicht. Ruby liefert den Wert nil. Das bedeutet, der Wert ist ungültig. Es liegt daran, dass die eckigen Klammern im Hash eine andere Bedeutung haben, als in einer Liste. In der Liste verwendest du sie für die Angabe des Index, im Hash hingegen für den Schlüssel (Key). Und im Hash haken gibt es den Key 1 nicht.

Es ist in Wirklichkeit so, dass es im Hash kein zweites Element gibt. Ein Hash hat keine festlegbare Reihenfolge wie eine Liste. Daher macht es bei einem Hash keinen Sinn, die Frage nach dem zweiten Element zu stellen. Die Frage müsste eher lauten: Gib mir den Wert zum Schlüssel soundso!.


puts "Der Wert zum Schlüssel 'Haus' im Hash ist " + haken["Haus"]

Suchen und Finden von Elementen

Mit dem Wissen, wie wir auf die einzelnen Elemente einer Liste und eines Hash zugreifen können, ist es nun gar nicht mehr so schwer, nach bestimmten Elementen in beiden Datenstrukturen zu suchen.

Das Suchen in der Liste könntest du etwa in folgender Methode ausdrücken:


def suche_in_liste_nach(wo, was)
  gefunden = false
  for element in wo do
    if element == was 
      gefunden = true
      break
    end
  end

  if gefunden
    puts "'" + was + "' ist drin!" 
  else
    puts "'" + was + "' ist NICHT drin!" 
  end
end

Suchst du nach dem Wert “Tisch”,


  suche_in_liste_nach(kette, "Tisch")

dann gibt die Methode aus, dass der gesuchte Wert (Variable was) nicht in der Liste wo enthalten ist. Beachte hier, dass die Variable kette, in der du die Liste definiert hast, an die Methode suche_in_liste_nach übergeben wird und innerhalb der Methode unter einem anderen Namen (nämlich wo) bekannt gemacht ist. Beide Variablen sind von einander abhängig, sie meinen also dieselbe Liste. Würde die Methode suche_in_liste_nach irgendetwas an der Liste wo verändern, würde die Liste kette davon auch berührt sein. Auch wenn die Variablennamen unterschiedlich sind, gibt es hier nur eine einzige Liste.

Hast du eine Idee, wie wir nach dem Wert Tisch in dem Hash suchen könnten? Bei einem Hash musst du dir zunächst klar machen, ob du nach einem Schlüssel suchst, oder nach einem Wert, wobei dir der Schlüssel dann ziemlich egal ist. Die Suche nach einem Schlüssel ist in einem Hash einfach. Es ist ausreichend, mit dem gesuchten Schlüssel einen Zugriff auf den möglichen Wert zu versuchen. Nur wenn der Wert nicht nil ist, können wir davon ausgehen, dass der Schlüssel wirklich existiert.


  suche_nach = "Tisch" 
  if haken[suche_nach] != nil
    puts "Den Schlüssel '" + suche_nach + "' gibt es!" 
  else
    puts "Den Schlüssel '" + suche_nach + "' gibt es nicht!" 
  end

Die Entscheidung also, ob ein gesuchter Key in einem Hash enthalten ist, ist ziemlich schnell durch einen einzigen Zugriff auf den Hash entscheidbar. Es ist diese Eigenschaft, die einen Hash so interessant macht und warum man sich am häufigsten für ihn entscheidet, wenn dieses Kriterium im Vordergrund steht. Bei einer Liste muss man notfalls immer alle Elemente anschauen, bevor man die Entscheidung entgültig treffen kann, bei einem Hash reicht ein Zugriff. Das spart in großen Programmen enorm Zeit.

Etwas mehr müssen wir tun, wenn wir den Schlüssel nicht kennen, aber wissen wollen, ob ein bestimmter Wert im Hash abgelegt ist. Gibt es im Hash haken für Livia einen Eintrag?


  suche_in_liste_nach(haken.values, "Livia")

Das sieht nur deswegen kürzer aus, weil wir hier unsere oben definierte Suchmethode für Listen wiederverwenden. Wie bitte? Liste? Wir suchen doch etwas in einem Hash, oder? Ich hatte dir in einer der letzten Lektionen bereits angedeutet, dass ein Hash im Prinzip aus zwei Listen besteht: einer Liste die alle Schlüssel als Elemente hat und einer zugehörigen Liste mit den jeweiligen Werten zu den einzelnen Schlüsseln. An die Liste aller Schlüssel kommst du mit der Anweisung


  haken.keys

heran, an die Liste der zugehörigen Werte mit


  haken.values

Und diese letztgenannte Liste durchsuchen wir nach dem gesuchten Wert Livia mit unserer oben definierten Suchmethode für Listen.

Hinzufügen und Löschen von Elementen

Verwende den Operator << zum Hinzufügen eines Elements in die Liste.


  kette << "Tisch" 
  suche_in_liste_nach(kette, "Tisch")

Lösche ein Element aus einer Liste, indem du an die Liste die Nachricht delete (engl. lösche!) schickst und in der Zusatzinformation der Nachricht den zu löschenden Wert angibst.


  kette.delete "Tisch" 
  suche_in_liste_nach(kette, "Tisch")

Füge einen neuen Wert in einen Hash ein, indem du einfach für den neuen Schlüssel einen Wert angibst. Beachte aber, dass wenn der Schlüssel vorher schon enthalten war, danach der neue Wert für diesen Schlüssel im Hash abgelegt wird.


  haken["Tisch"] = "Hans" 

Lösche ein Schlüssel-Wert Paar aus dem Hash ebenfalls mit der delete Nachricht. Die Zusatzinformation muss aber den Namen des Schlüssels angeben, nicht den des zugehörigen Wertes.


  haken.delete "Tisch" 

Ein komplizierteres Beispiel

Ich möchte dich aus dieser Lektion nicht ohne ein etwas komplizierteres Beispiel entlassen, mit dem ich dir zeigen möchte, dass die Entscheidung, ob man eine Liste oder einen Hash verwendet nicht immer ganz so leicht zu treffen ist.

Nicht selten hast du zunächst eine Liste, dann aber merkst du, dass du eigentlich zu jedem Element in der Liste einen weiteren Wert zuordnen möchtest. Klarer Fall, wir nehmen einen Hash, beispielsweise mit den Werten der Liste als Schlüssel. Das geht natürlich nur, wenn in der Liste alle Elemente untereinander verschieden sind, also in der Liste kein Wert doppelt vorkommt.

Stell dir vor, du möchtest dir mit Ruby für jeden Wochentag einer bestimmten Woche die Farbe des Pullovers deiner Erzieherin im Kindergarten merken (oder deiner Lehrerin, wenn du schon zur Schule gehst, oder deiner Chefin, falls das nicht mehr der Fall ist, oder …). Liste oder Hash? Das ist hier die Frage.

Pulloverfarbe mit zwei Listen

Fangen wir mit dem naheliegendsten an: wir nehmen zwei Listen. Eine für die Wochentage, und eine für die Farben an diesen Tagen, wobei die Reihenfolge hier in beiden Listen korrespondiert. Das soll heißen, dass erste Element ist der Montag in der Liste der Wochentage, also muss auch die erste Farbe in der Farbenliste die Pulloverfarbe für den Montag sein. Klar, oder? Geben wir auch gleich schon man alle korrespondierenden Werte nach der Definition unserer Listen mit aus.


  woche = ["Mo", "Di", "Mi", "Do", "Fr"]
  farbe = ["gelb", "blau", "pink", "rot", "grün"]

  tag_zaehler = 0
  for tag in woche do
    puts "Am " + tag + " ist die Farbe " + farbe[tag_zaehler]
    tag_zaehler = tag_zaehler + 1
  end

Du siehst, wir verwenden für beide Listen einen gemeinsamen Index mit dem Namen tag_zaehler, der uns die beiden Listen zusammenhält. Die Wochentage könnten somit auch in einer anderen als der üblichen Reihenfolge beginnend bei Montag bis Freitag in der Wochenliste stehen, solange die Farbwerte auch genauso in der Farbliste angeordnet würden.

Welche Farbe hatte ihr Pullover am Mittwoch? Wir suchen zunächst nach dem “Mi” in der Wochenliste, merken uns den Index und schauen dann mit diesem Index einmal in der Farbliste nach.


  woche = ["Mo", "Di", "Mi", "Do", "Fr"]
  farbe = ["gelb", "blau", "pink", "rot", "grün"]

  such_tag = "Mi" 
  tag_zaehler = 0
  for tag in woche do
    break if tag == such_tag
    tag_zaehler = tag_zaehler + 1
  end

  puts "Am " + such_tag + " ist die Farbe " + farbe[tag_zaehler] + "!" 

Pulloverfarbe mit einer Liste

Geht das ganze auch mit einer Liste? Warum nicht? Vielleicht so:

  pullover = ["Mo", "gelb", "Di", "blau", "Mi", "pink", "Do", "rot", "Fr", "grün"]

  tag_zaehler = 0
  1.upto(5) do
    tag   = pullover[tag_zaehler]
    farbe = pullover[tag_zaehler + 1]
    puts "Am " + tag + " ist die Farbe " + farbe
    tag_zaehler = tag_zaehler + 2
  end

Hintereinanderweg schreiben wir abwechselnd Tag und Farbe in eine gemeinsame Liste. Natürlich müssen wir nun mit dem Index etwas aufpassen. Der Index für die Farbe ist immer um genau Eins größer als der Index des Wochentages. Außerdem müssen wir am Ende unserer Schleife den Index für den Tag nicht nur um Eins, sondern gleich um Zwei erhöhen, um jeweils die Farbe zu überspringen und zum nächsten Tag zu gelangen.

Welche Farbe hatte ihr Pullover am Mittwoch? Wir suchen zunächst nach dem “Mi” in der gemeinsamen Liste, merken uns den Index und schauen dann mit dem um Eins erhöhten Index einmal in derselben Liste nach.


  pullover = ["Mo", "gelb", "Di", "blau", "Mi", "pink", "Do", "rot", "Fr", "grün"]

  such_tag = "Mi" 
  tag_zaehler = 0
  1.upto(5) do
    break if pullover[tag_zaehler] == such_tag
    tag_zaehler = tag_zaehler + 2
  end

  puts "Am " + such_tag + " ist die Farbe " + pullover[tag_zaehler + 1] + "!" 

Du siehst auch hier eine andere Art der Schleife. Upto bedeutet bis zu. Die Schleife läuft also von 1 bis zu 5, genau 5 mal durch. Wenn du dein Pulloverprotokoll für mehr als 5 Wochentage aufzeichnest, dann musst du die Zahlen hier entsprechend anpassen.

Pulloverfarbe mit vielen Listen in einer Liste

Du könntest natürlich auch die zusammengehörigen Werte in einer Liste nicht nur einfach hintereinanderweg schreiben, sondern zunächst paarweise in einer kleinen Liste gruppieren und dann diese kleinen Listen in einer großen Liste zusammenfassen. Etwa so:


  pullover = [
    ["Mo", "gelb"], 
    ["Di", "blau"],
    ["Mi", "pink"],
    ["Do", "rot"],
    ["Fr", "grün"],
  ]
  for paar in pullover do
    puts "Am " + paar[0] + " ist die Farbe " + paar[1]
  end

Beachte bitte, dass es sich hier bei der Variable pullover nicht um einen Hash handelt, sondern eine Liste! Ich habe die Schreibweise zwar so ähnlich wie bei einem Hash angeordnet, aber du erkennst an den eckigen Klammern, dass es nur Listen sind!

Welche Farbe hatte ihr Pullover am Mittwoch? Wir suchen das Wertepaar, das an der erste Stelle (dem Index 0 in der kleinen Liste) den Werte “Mi” hat und geben für die gefundene kleine Liste den Wert beim Index 1 aus.


  pullover = [
    ["Mo", "gelb"], 
    ["Di", "blau"],
    ["Mi", "pink"],
    ["Do", "rot"],
    ["Fr", "grün"],
  ]

  such_tag = "Mi" 
  for paar in pullover do
    if paar[0] == such_tag
      puts "Am " + such_tag + " ist die Farbe " + paar[1] + "!" 
      break
    end
  end

Pulloverfarbe mit Hash

Endlich. Wir nehmen einen Hash. Es scheint sich irgendwie aufzudrängen. Das mit dem Index scheint doch sehr kompliziert und unübersichtlich.


  pullover_hash = {
    "Mo" => "gelb",
    "Di" => "blau", 
    "Mi" => "pink", 
    "Do" => "rot",
    "Fr" => "grün",
  }

  for tag in pullover_hash.keys do 
    puts "Am " + tag + " ist die Farbe " + pullover_hash[tag]
  end

Die Frage Welche Farbe hatte ihr Pullover am Mittwoch? ist nun in nur einer Zeile beantwortbar.


  pullover_hash = {
    "Mo" => "gelb",
    "Di" => "blau", 
    "Mi" => "pink", 
    "Do" => "rot",
    "Fr" => "grün",
  }

  such_tag = "Mi" 
  puts "Am " + such_tag + " ist die Farbe " + pullover_hash[such_tag]

Warum nicht gleich so? Wie gesagt, die Lage der Dinge ist nicht immer so einfach. Was ist, wenn du dich für einen Hash entschieden hast, später aber neben der Farbe auch noch die Schuhe mit in dein Programm aufnehmen möchtest (du kennst das von Mama: Frauen haben gewöhnlich mindestens 5 Paar Schuhe)? Dann bist du mit einem Hash ziemlich aufgeschmissen. Überlege dir, wie es vielleicht doch mit einem Hash gehen könnte! Kleiner Tipp: der Wert, der zu einem Schlüssel in einem Hash abgelegt ist, kann auch eine Liste sein.

Üben mit Peter und Livia

Peter und Livia haben heute keine Lust zum Üben. Sie freuen sich schon auf das Spiel, das sie in der nächsten Lektion mit dir zu programmieren beginnen werden und sind vor lauter Aufregung zu keiner Übung mehr fähig.

Über Listen - Theorie Lektion 5

Erstellt von Frithjof Thu, 02 Aug 2007 20:01:00 GMT

Diese Lektion und vielleicht noch eine weitere, dann fängst du aber wirklich an, ein Spiel in Ruby zu programmieren. Jetzt lernst du ein sehr wichtiges Dings. Das Dings gehört zu den sogenannten Datenstrukturen.

Du weißt bereits, wie man eine Zahl oder eine Zeichenkette in Ruby verwendet. Was aber, wenn du gleich mehrere Zahlen oder Zeichenketten als ein Ganzes verwenden möchtest, sozusagen eine Liste mit Zahlen oder Zeichenketten?

Einfache Liste in Ruby

Schau dir diesen Code an:


# theorie_05.rb

zahlen_liste = [2, 4, 6, 8, 10]

planeten_liste = [
  "Merkur",  "Venus", 
  "Erde",    "Mars", 
  "Jupiter", "Saturn", 
  "Uranus", "Neptun",
]

irgendwas_liste = ["Mars", 95, "95", 95, "Quark"]

leere_liste = []

Du siehst, eine Liste ist ganz einfach zu erstellen. Der Anfang einer Liste wird mit der öffnenden eckigen Klammer [ markiert, das Ende der Liste mit der schließenden eckigen Klammer ]. Zwischen die Klammern schreibst die Glieder der Liste und trennst sie jeweils mit einem Komma ab. Ein Komma darf auch nach dem letzten Glied in einer Liste stehen, aber nicht vor dem ersten. Man bezeichnet die Dinge in einer Liste als Mitglieder oder Elemente der Liste. Eine Liste ohne Elemente heißt die leere Liste.

Eine Liste kann aus einheitlichen Elementen bestehen, also bspw. nur Zahlen. Sie kann aber auch Elemente verschiedenen Typs enthalten, also bspw. Zeichenketten und Zahlen. Ein Element einer Liste kann sogar selbst wieder eine Liste sein!

Eine Liste kann mehrere identische Elemente enthalten. Bspw. in der irgendwas_liste ist die 95 zweimal enthalten. Oder dreimal? Nein, für Ruby ist das Element 95 eine Zahl, das Element "95" aber eine Zeichenkette. Daher ist die Zahl 95 nur zweimal enthalten.

Verteile die Elemente einer Liste über mehrere Zeilen, damit es besser lesbar ist, was in der Liste so alles enthalten ist. Du brauchst die Liste also nicht in eine einzige Zeile zu quetschen.

Listen nennt man auch Array (engl. für Anordung, Reihe) oder Vektor. Genaugenommen ist eine Liste etwas mehr als ein Array. Bei einer Liste kennt jedes Element ihre unmittelbaren Nachbarelemente, entweder das davor und das danach oder nur eines von beiden. Das was wir hier in diesem Artikel als Liste kennen lernen ist daher genaugenommen nur ein Array, weil die Elemente sich selbst untereinander nicht kennen. Wir bleiben aber hier trotzdem bei dem Begriff Liste, weil man eher begreift, was gemeint ist.

Ausgabe einer einfachen Liste

Angenommen wir definieren eine Einkaufsliste mit den Elementen Butter, Milch, Honig und Brot. Dann können wir die Liste natürlich einfach mit dem puts Befehl ausgeben. Dabei wird jedes Element der Liste in einer neuen Zeile ausgegeben.

Ruby bietet dir aber noch eine schönere Ausgabe der Liste an. Auf englisch heißt das pretty print (schöner Druck). Verwende statt puts den Befehl pp (dabei steht pp für pretty print). Bevor du diesen Befehl in deinem Programm verwenden kannst, muss du ihn im Programm bekannt machen über ein require ‘pp’ am Anfang der Datei:


require 'pp'

# Einzelne Einkaufslisten
tegut = ["Butter", "Milch", "Honig", "Brot"]
obi   = ["Schrauben", "Leimholz", "Kabel", "Lampe"]
dm    = ["Duschbad für Mama",    "Deo für Papa", 
         "Haarspange für Livia", "Zahnbürste für Peter"]

# Gesamte Einkaufsliste, enthält die einzelnen Listen
einkauf = [tegut, obi, dm]

# Normale Ausgabe
puts einkauf

# Schickere Ausgabe mit pretty print
pp einkauf

# Ausgabe des dritten Elementes der Liste
pp einkauf[2]

Du siehst hier auch, dass eine Liste als Elemente wiederum Listen enthalten kann. Die Liste obi mit ihren Elementen "Schrauben", "Leimholz", "Kabel", "Lampe" wird in der Liste einkauf als zweites Element gespeichert.

Die gesamte Liste auszugeben ist gut und schön. Häufiger jedoch möchtest du nur bestimmte Elemente der Liste ausgeben. Ruby nummeriert die Elemente durch, sodass du einfach nur die Nummer des Elementes angeben musst. Das machst du, indem du direkt an den Variablennamen der Liste die gewünschte Nummer in eckige Klammern anhängst.

Aber Achtung: Ruby beginnt beim Durchnummerieren der Elemente in einer Liste immer mit 0, nicht mit 1! Möchtest du das dritte Element in der Einkaufsliste einkauf ausgeben, musst du somit einkauf[2] schreiben, nicht einkauf[3], weil das erste Element in der Liste nämlich nicht einkauf[1], sondern einkauf[0] ist.

Hash – Liste mit Index

Ruby kennt noch eine weitere wichtige listenähnliche Datenstruktur. Es ist eigentlich eine Art Doppelliste bestehend aus zwei Listen: eine Liste mit Namen und eine Liste, die für jeden Namen den zugehörigen Wert enthält. Den Namen nennt man auch Schlüssel oder englisch Key oder Index. Ein bestimmter Key darf in dieser Liste nur höchstens einmal vorkommen. Daher heißt diese Datenstruktur auch Indextabelle oder englisch Hashtable oder einfach nur ein Hash. Verwende einen Hash immer dann, wenn du schnell über einen Schlüsselnamen auf einen zugehörigen Wert zugreifen möchtest.

Der folgende Hash legt für jeden Namen eines deutschen Zahlwortes oder einer natürlichen Zahl von 1 bis 10 als zugehörigen Wert das Zahlwort in spanischer Sprache ab.


require 'pp'

deutsch_spanisch = {
  1        => "uno",
  "eins"   => "uno",
  2        => "dos",
  "zwei"   => "dos",
  3        => "tres",
  "drei"   => "tres",
  4        => "cuatro",
  "vier"   => "cuatro",
  5        => "cinco", 
  "fuenf"  => "cinco", 
  6        => "seis",
  "sechs"  => "seis",
  7        => "siete",
  "sieben" => "siete",
  8        => "ocho",
  "acht"   => "ocho",
  9        => "nueve",
  "neun"   => "nueve",
  10       => "diez",
  "zehn"   => "diez",
}
pp deutsch_spanisch
puts "Vier heisst auf Spanisch #{deutsch_spanisch['vier']}." 
puts "Acht heisst auf Spanisch #{deutsch_spanisch[8]}." 

Ein Hash wird ähnlich einer Liste definiert aber mit folgenden Unterschieden. Der Anfang und das Ende des Hash werden mit geschweiften Klammern (anstatt eckiger) markiert. Die Elemente des Hash werden zwar auch mit Komma getrennt, aber ein Element besteht immer aus einem Name-Wert-Paar. Zuerst schreibst du den Namen hin (eine Zahl oder eine Zeichenkette, oder …), dann einen nach rechts gerichteten Pfeil => und auf die rechte Seite des Pfeils schreibst du den Wert. Lies den Pfeil als wenn du sprichst: dem Namen (oder Key) sowieso ordne ich den Wert soundso zu.

Du siehst in unserem deutsch_spanisch Hash auch, dass es zwar gleiche Werte auf der rechten Seite geben kann, auf der linken Seite darf jeder Key aber nur höchstens einmal vorkommen. Kommt ein Key aber trotzdem auf der linken Seite mehrmals vor, dann speichert Ruby nur den zuletzt genannten Wert im Hash ab.


require 'pp'

deutsch_spanisch = {
  "acht" => "ocho",
  "acht" => "otscho",
}
puts "Acht heisst auf Spanisch #{deutsch_spanisch['acht']}." 

Dieser Hash hat nur ein Element! Der Wert für den Key acht wird beim zweiten mal überschrieben und hat am Ende nur einen Wert otscho.

Durchgehen von Listen und Hashes

Nicht selten muss man eine Liste durchgehen und mit jedem Element der Liste etwas bestimmtes unternehmen. Oder man möchte für alle Keys in einem Hash die Werte untersuchen. Schauen wir uns zunächst an, wie man für eine Liste alle Elemente durchgeht:



# Schleife über eine Liste

planeten = [
  "Merkur",  "Venus", 
  "Erde",    "Mars", 
  "Jupiter", "Saturn", 
  "Uranus", "Neptun",
]

for planet in planeten
  # Nur die Planeten ausgeben, in deren Namen ein e vorkommt.
  if planet.include?("e")
    puts planet
  end
end

Das Durchgehen einer Liste nennt man auch einen Loop über eine Liste machen. Loop (englisch für Schleife) deswegen, weil man schleifenartig ein Element nach dem anderen abklappert. Die Schleife beginnt mit dem for planet in planeten und endet mit end. Am Beginn der Schleife wird der Variablen planet bei jedem Durchgang der Wert des jeweils nächsten Elements zugewiesen. Diese Variable ist nur innerhalb der Schleife bekannt. Man nennt sie daher auch Schleifenvariable. Wenn alle Elemente in der Schleife behandelt wurden, setzt Ruby das Programm nach dem end fort.

Für einen Hash könnte ein Schleifendurchgang etwa so aussehen.



# Schleife über einen Hash

de_esp = {
  "eins"   => "uno",
  "zwei"   => "dos",
  "drei"   => "tres",
  "vier"   => "cuatro",
  "fuenf"  => "cinco", 
  "sechs"  => "seis",
  "sieben" => "siete",
  "acht"   => "ocho",
  "neun"   => "nueve",
  "zehn"   => "diez",
}

for key in de_esp.keys
  if de_esp[key].include?("t")
    puts de_esp[key]
  end
end

Die Liste aller Keys für den Hash de_esp bekommen wir mit de_esp.keys. Diese Liste gehen wir durch und verwenden den jeweiligen Key dazu, uns den zugehörigen Wert aus dem Hash zu holen—hier das Zahlwort auf Spanisch. Enthält der Hashwert (das spanische Zahlwort) ein t im Namen, dann geben wir es mit puts aus.

Natürlich hat Ruby noch mehrer Möglichkeiten, Schleifen über Listen und Hashes zu machen. Aber die hier vorgestellte for-Schleife ist gut lesbar und fürs erste ausreichend.

Üben mit Peter und Livia

Livia: Schreibe die Einkaufsliste von oben als Hash um. Verwende dabei die Variablennamen der einzelnen Einkaufslisten (tegut, obi, dm) als Keys im Hash! Was fällt dir bei der pretty print Ausgabe bezüglich der Reihenfolgen von Liste und Hash auf?

Peter: Zähle bitte auf, wozu man alles die eckigen Klammern [ ] bei Listen und Hashes benutzt!