Komplexe Datentypen
*Hashmaps
Diese kann man sich von der Funktionsweise wie Wörterbücher vorstellen. Sie heißen auch in vielen anderen Programmiersprachen "Dictionary". Der Nutzen der dabei erzielt wird, ist jedem Schlüssel einen Wert zuzuweisen. Das ist zB brauchbar für ein Adressbuch, oder ein Übersetzungstool, eine Zuordnung für Namen und Ränge, ein Zeugnis mit Benotung. Ihr könnt euch vermutlich vorstellen, wohin die Reise geht.
Für HashMaps gibt es mehrere Herangehensweisen, je nachdem was man braucht, oder was besser für den Anwendungsfall geeignet ist. Im klassischen Sinn erstellt man am Anfang eine neue HashMap. Oh, genau. HashMaps benötigen wieder eine Bibliothek eingebunden vor der main().
use::std::collections::HashMap;
Und dann gehts los.
use::std::collections::HashMap;
fn main(){
let mut namen = HashMap::new();
}
Aber leider meckert da der Compiler noch.Ohne Angaben von Inhalten, ist er nicht zufrieden.
Dabei weiß ich aber bereits, dass der Wunsch nach Typverifzierung nicht wirklich sein Problem ist. Ich meine, es wird nicht kritisiert, wenn ich es einfüge, obwohl ich es nicht dringend brauche. Es stellt offenbar ein Minimum an Angaben dar, damit er zufrieden ist. (zum Angeben von Datentypen kommen wir dann noch) Vorerst erweitern wir die HashMap mit der Anweisung .insert()
use::std::collections::HashMap;
fn main(){
let mut namen= HashMap::new();
namen.insert("Max",1);
namen.insert("Moritz",2);
}
So werden Schlüssel und Wert jeweils in die HashMap-Datenstruktur eingebaut.
In diesem Zusammenhang kann ich auch gleich sagen, dass mit .remove() wieder ein Datensatz entfernt wird.
namen.remove("Moritz");
Und schon isser wieder weg, der Moritz. Übrigens wird nur der Schlüssel gelöscht, der Wert selbst dahinter eliminiert sich damit automatisch.
Das ändern eines Werts von einem Schlüssel ist auch sehr einfach. Es passiert ebenfalls mit .insert()
namen.insert("Max",4);
nun hat Max den Wert 4, Max mit 1 gibts somit nicht mehr. Machen wir mal einen Blick auf die Ausgabe von der namen-HashMap.
In der HashMap befindet sich nur mehr Max, 4. Moritz war da, Moritz wurde gelöscht. Max war 1, Max ist jetzt 4. Ich glaube, das stellt noch nicht so eine große Herausforderung dar. Vielleicht ist es sogar kompliziert, weil man das Einfache hier nicht erwarten würde.
So, jetzt zur Formulierung einer HashMap mit gewollten Datentypen. Inwiefern das nötig ist, weiß ich selbst noch nicht so genau. Immerhin kann der Compiler selbst die Datentypen gut erkennen. Mit einer Ausnahme, wo es relevant sein könnte. Nämlich dann, wenn die HashMap noch ohne weiteren Inhalt stehen soll. ...Jetzt im Nachhinein, ist mir evtl. doch ein Nutzen eingefallen. Wenn man die HashMap im Vorfeld formuliert, schreit der Compiler, wenn man später den falschen (vermutlich überdachten) Datentypen verwendet.
let mut empty_HashMap:HashMap<&str,i32> = HashMap::new();
Ja, ich weiß. Das ist schon wieder so eine ewig lange Litanei. Wie kann man sich das am besten merken? Theoretisch müsste hier der Variablenname noch nicht mut gesetzt werden, weil eine Änderung der HashMap vielleicht erwartet wird, aber noch nicht passiert ist. Gut, machen wir das Schritt für Schritt.
*Die Variable wird deklariert
let mut empty_HashMap
*mit einem Doppelpunkt folgt die Typannotation, HashMap<Typ1,Typ2>, ist die Methode wie der Datentyp einer HashMap angegeben wird. (HashMap muss man wirklich so schreiben, sonst funktioniert es nicht)
:HashMap<&str, i32>
*Danach folgt die Zuweisung.
=HashMap::new();
Und schon steht eine leere HashMap rum, fidibum. Klarerweise muss man sich an die eigenen Vorgaben halten, die man angegeben hat. Das hier zB gefällt ihm gar nicht.
let mut empty_HashMap:HashMap<&str,i32> = HashMap::new();
empty_HashMap.insert("Alta","Walta");
Da motzt er
Also, bin ich nett und geb ihm den Schlüssel und Wert der gewünschten Datentypen mit.
let mut empty_HashMap:HashMap<&str,i32> = HashMap::new();
empty_HashMap.insert("Walter",2);
println!("{:?}",empty_HashMap);
Ausgabe
So weit, so gut. Im Laufe der Zeit möchten wir vielleicht mal was suchen in der Hashmap. Wir wollen wissen, ob es einen bestimmten Schlüssel gibt. Obwohl sich das bereits abgezeichnet hat; Es kann nur EINEN Schlüssel geben von einer Sache. Diesem können verschiedene Werte zugewiesen werden. Ok, wie sucht man nun nach solchen Schlüsseln. Im Zentrum stehen die Schlüsselwörter .get() und Some (nein, das Wortspiel war nicht beabsichtigt). Wobei get tatsächlich für das Herausfischen, oder holen steht und Some als ein Platzhalterprogramm, samt einer zugewiesenen Variable, wenn etwas gefunden wurde. Aufgebaut wird es innerhalb einer if-Anweisung. Damit mehr Daten zur Verfügung stehen, habe ich der HashMap namen,noch die Einträge
namen.insert("Susi",5);
namen.insert("Walter",5);
verpasst.
Suchen wir also mal nach der Susi
let key = "Susi";
if let Some(value) =namen.get(key){
println!("{:?} wurde für {} in der HashMap'namen' gefunden", value,key );
}
else{
println!("Es wurde kein Eintrag in der HashMap 'namen' für {} gefunden", key);
}
Ausgabe
oder ändern wir mal den Key
let key = "Alta";
Ausgabe
funktioniert also. Ein kleiner Hinweis hier: Den Key muss man nicht zwingend als variable speichern. Eine andere Methode könnte auch so aussehen.
if let Some(value) = namen.get("Susi")
Mir gefällt es aber besser den Schlüssel in eine Variable zu speichern, sonst ließe es sich im println! (wie hier in diesem Beispiel), nicht ausgeben.
Eine weitere Spielerei ist, passende Datensätze, von anderen Datenstrukturen in eine HashMap zu verwandeln. Und zwar, indem einfach die andere Datenstruktur mit .... Hashmap::from(name_der_anderen_Struktur) eingelesen wird.
let extern_array = [
("gut","good"),
("schlecht", "bad"),
("neu", "new"),
("Tag", "day"),
("Nacht", "night")
];
let import_extern_array = HashMap::from(extern_array);
let search = "gut";
if let Some(value) = import_extern_array.get(search){
println!("{} = {}", search, value);
}
else{
println!("Es wurde kein Eintrag mit {} gefunden", search);
}
Da wurde gleich doppelt gemoppelt. Zum einen haben wir Tuples innerhalb eines Arrays. Diese Datenstruktur schließt man wieder mit einem Semikolon und die einzelnen Elemente innerhalb, die sonst ein Semikolon hätten, werden mit einem simplen Beistrich getrennt. Danach wird dieses Konstrukt für eine HashMap eingelesen und ist für die if let Some... Anweisung verfügbar.
Ausgabe
So, dann noch ein Versuch, wenn der Eintrag nicht vorhanden ist.
let search = "blabla";
Dann gibt es noch etwas, das ich selbst als eine Art Haarspalterei empfinde. Es lässt sich nämlich auch prüfen, ob ein Schlüssel existiert, ohne den Wert dabei mitzuliefern. Das funktioniert mit .contains.key(). Und widerrum könnte man die Klammern als Platzhalter für einen Eintrag verwenden .contains.key(Eintrag). Es soll etwas schneller vonstatten gehen, als wenn der Wert mitgeliefert wird.
let search = "Hallo";
if import_extern_array.contains_key(search){
println!("{} wurde gefunden im Wörterbuch", search);
}
else{
println!("{} wurde nicht gefunden im Wörterbuch", search);
}
Ausgabe
Anscheinend habe ich die Angewohnheit lauter Negativbeispiele ("Wurde nicht gefunden") zu bringen. Ich gelobe Besserung, bzw. beim nächsten mal Ausgleich.
Das wars im Grunde mit den HashMaps. Wie gewohnt wieder die Zusammenfassung.
Ah, da kann ich noch ein paar Positiveinträge hineinschummeln.
use::std::collections::HashMap;
fn main(){
let mut namen= HashMap::new();
namen.insert("Max",1);
namen.insert("Moritz",2);
namen.remove("Moritz");
namen.insert("Max",4);
namen.insert("Susi",5);
namen.insert("Walter",5);
println!("{:?}",namen);
let mut empty_HashMap:HashMap<&str,i32> = HashMap::new();
empty_HashMap.insert("Walter",2);
println!("{:?}",empty_HashMap);
let key = "Alta";
if let Some(value) =namen.get(key){
println!("{:?} wurde für {} in der HashMap'namen' gefunden", value,key );
}
else{
println!("Es wurde kein Eintrag in der HashMap 'namen' für {} gefunden", key);
}
let extern_array = [
("gut","good"),
("schlecht", "bad"),
("neu", "new"),
("Tag", "day"),
("Nacht", "night")
];
let import_extern_array = HashMap::from(extern_array);
let search = "Nacht";
if let Some(value) = import_extern_array.get(search){
println!("{} = {}", search, value);
}
else{
println!("Es wurde kein Eintrag mit {} gefunden", search);
}
let search = "Tag";
if import_extern_array.contains_key(search){
println!("{} wurde gefunden im Wörterbuch", search);
}
else{
println!("{} wurde nicht gefunden im Wörterbuch", search);
}
}
Vom Editor