021 Übung lua IiI:
ASSOZIATIVE TABELLEN:
local my_table = { "Apfel", "Birne", 42 } print(my_table[2])
local my_table = { last_name = "Müller", address = "Hannover", postal_code = 30449 }
local my_output = my_table["last_name"] print(my_output)
local my_output = my_table.last_name print(my_output)
my_table["last_name"] = "Meier" my_table.address = "Berlin"
Man kann auch komplett neue Wertpaare in der Tabelle erschaffen:
my_table["first_name"] = "Thomas" my_table.street = "Lua-Weg 5.1"
assoziative Tabellen und Schleifen:
for i = 1, 3 do print(my_table[i]) end
Können wir das Problem vielleicht mit »ipairs« lösen:
for i, v in ipairs(my_table) do print(my_table[i]) end
Antwort – aber bitte überlege zunächst einmal selber:
Auch »ipairs« eignet sich nicht, da hier ebenfalls die Variable i beim Durchlauf immer nur mit einem Zahlenwert versehen wird; es wird aber der jeweils passende Wort-Bezeichner benötigt.
Tabellenzugriff mit »pairs«:
local my_table = { last_name = "Müller", place = "Hannover", postal_code = 30449 } for k, v in pairs(my_table) do print(k .. ": " .. v) end
for k in pairs(my_table) do print(k .. ": " .. my_table[k]) end
AUFGABE: Schreibe ein Programm, dass Nutzer:innen auffordert, Vorname und Wohnort einzugeben. Diese Eingaben sollen in eine assoziative Tabellen geschrieben und dann ausgegeben werden. Du benötigst »io.read()«:
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local input = {}
print("Bitte gib Deinen Namen ein:")
input["Name"] = io.read() -- Zugriff auf Tabelle mit eckigen Klammern
print("Bitte gib Deinen Wohnort ein:")
input.Ort = io.read() -- Zugriff auf Tabelle mit Punktnotation
print("Hallo " .. input.Name .. " aus " .. input["Ort"])
----------------------- Alternative mit while-Schleife -----------------------
local input_while = {}
while true do -- Endlosschleife mit »while«
print("Bitte gib Deinen Namen ein:")
input_while["Name"] = io.read()
print("Bitte gib Deinen Wohnort ein:")
input_while.Ort = io.read()
break -- Endlosschleife mit »break« beenden, aber da gäbe es auch andere Möglichkeiten ...
end
print("Hallo " .. input_while.Name .. " aus " .. input_while["Ort"])
local dichter = { Klassiker = "Goethe", Romantiker = "Tieck", Realist ="Fontane" }
Fontane ist ein Realist - Goethe ist ein Klassiker - Tieck ist ein Romantiker
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local ausgabe = ""
local dichter = {
Klassiker = "Goethe",
Romantiker = "Tieck",
Realist ="Fontane"
}
for k, v in pairs(dichter) do
ausgabe = ausgabe .. " - " .. v .. " ist ein " .. k
end
print(ausgabe)
ausgabe = "" -- Ausgabe zurücksetzen für eventuell weitere Durchläufe
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
-------------------------------------- Lösungs-Variante mit if-Abfrage --------------------------------------
local ausgabe = ""
local dichter = {
Klassiker = "Goethe",
Romantiker = "Tieck",
Realist ="Fontane"
}
for k, v in pairs(dichter) do
if ausgabe == "" then -- erster Durchlauf, es steht noch nichts in der Variablen »ausgabe»
ausgabe = v .. " ist ein " .. k
else
ausgabe = ausgabe .. " - " .. v .. " ist ein " .. k
end
end
print(ausgabe)
ausgabe = "" -- Ausgabe zurücksetzen für eventuell weitere Durchläufe
---------------------------------------- Lösungs-Variante mit Zähler ----------------------------------------
local ausgabe = ""
local zaehler = 0
local dichter = {
Klassiker = "Goethe",
Romantiker = "Tieck",
Realist ="Fontane"
}
for k, v in pairs(dichter) do
if zaehler == 0 then -- erster Durchlauf, es steht noch nichts in der Variablen »ausgabe»
ausgabe = v .. " ist ein " .. k
zaehler = zaehler + 1 -- Zähler inkrementieren
else
ausgabe = ausgabe .. " - " .. v .. " ist ein " .. k
end
end
print(ausgabe)
zaehler = 0 -- Zähler zurücksetzen für eventuell weitere Durchläufe
ausgabe = "" -- Ausgabe zurücksetzen für eventuell weitere Durchläufe
Exkurs: assoziative Tabellen in Minetest
Tipp: Falls der Quellcode nicht gut lesbar ist, lade ihn Dir mit Rechtsklick und »Bild speichern unter …« auf Deine Festplatte.
Operationen mit Zeichenketten:
Verschaffen wir uns zunächst einen kleinen Überblick über die gängigsten Lua-Funktion zur Stringmanipulation:
Eine bestimmte Zeichenkette finden:
local my_string = "Hallo Welt" local my_pattern = "Welt" string.find(my_string, my_pattern)
local my_string = "Hallo Welt" local my_pattern = "Welt" local start_index, end_index = string.find(my_string, my_pattern) print("Startindex: " .. start_index .. "\nEndindex: " .. end_index)
print(string.find(my_string, my_pattern))
my_string:find(my_pattern)
local my_string = "Hallo Welt" local my_pattern = "Welt" local start_index, end_index = string.find(my_string, my_pattern) print(my_string:sub(start_index, end_index))
Teilstrings mit Pattern suchen und ausgeben:
local my_string = "Hallo Welt" local my_pattern = "Welt" local my_match = my_string:match(my_pattern) print(my_match)
Aber wie bringt man »Lua« dazu, das abstrakte Muster »Suche nach einem ganzen Wort« zu verwenden? Hier kommen sogenannte »character classes« ins Spiel. Als Beispiel steht »%w« für alle »alphanumerische Zeichen«, vereinfacht gesagt alle Buchstaben und Zahlen. Es gibt eine ganze Reihe solcher »character classes«:
Character Class | Suchbereich |
%a | Buchstaben (A-Z, a-z) |
%c | Kontrollzeichen (\n, \t, \r, ...) |
%d | Zahlen (0-9) |
%l | Kleinbuchstaben (a-z) |
%p | Satzzeichen (!, ?, &, ...) |
%s | Leerzeichen |
%u | Großbuchstaben (A-Z)) |
%w | alphanumerische Zeichen (A-Z, a-z, 0-9) |
%x | hexadezimale Zahlen (0-9, A-F, a-f) |
%z | Steuerzeichen an der Code-Position 0 |
. | jedes beliebige Zeichen |
Hinweis: Ersetzt man den Kleinbuchstaben der »character class« durch einen Großbuchstaben sucht die »character class« nach ihrem Gegenteil. Mit %D würden also alle Nicht-Zahlen ausgegeben. |
Schauen wir einmal, was passiert, wenn wir als Suchmuster »%w« in unser Beispiel eintragen:
local my_string = "Hallo Welt" local my_pattern = "%w" local my_match = my_string:match(my_pattern) print(my_match)
Magic Charakter | Bedeutung |
* | finde Muster null oder mehrmals, so oft wie möglich |
+ | finde Muster ein oder mehrmals, so oft wie möglich |
- | finde Muster null oder mehrmals, so selten wie möglich |
? | Muster kann optional vorkommen, muss aber nicht |
local my_string = "Hallo Welt" local my_pattern = "%w+" local my_match = my_string:match(my_pattern) print(my_match)
AUFGABE: Bei einem entsprechenden Aufbau des Suchmuster gibt »string.match()« auch mehr als nur einen Wert zurück. Kannst Du mit Hilfe von »character classes« und »magic characters« ein Muster entwerfen, so dass am Ende in der Variablen »my_match« sowohl »Hallo« als auch »Welt« steht?
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local my_string = "Hallo Welt"
local my_pattern = "%w+%s%w+"
local my_match = my_string:match(my_pattern)
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local my_string = "Hallo Welt"
local my_pattern = "%s%w+"
local my_match = my_string:match(my_pattern)
Diese Lösung hat allerdings noch einen kleinen Schönheitsfehler. Genau genommen wird nämlich »LEERZEICHENWelt« ausgegeben. Wir schauen uns gleich an, wie man mit Hilfe von »Captures« jeden gefundenen Teilstring in eine extra Variable schreiben kann.
ARBEITEN MIT CAPTURES:
lokale Variable1, Variable2 = Zeichenkette:match("Muster1-Leerzeichen-Muster2")
local my_string = "Hallo Welt" local my_pattern = "(%w+)%s(%w+)" local my_match1, my_match2 = my_string:match(my_pattern) print(my_match1 .. "\n" .. my_match2)
"(%w+)(%s)(%w+)" "(%w+)%w%s(%w+)"
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local my_string = "Hallo Welt"
local my_pattern = "%s(%w+)"
local my_match = my_string:match(my_pattern)
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
-------------------------- Variante 1: Bindestrich entfernen mit der Funktion »string.match()« --------------------------
local ausgabe = ""
local dichter = {
Klassiker = "Goethe",
Romantiker = "Tieck",
Realist ="Fontane"
}
for k, v in pairs(dichter) do
ausgabe = ausgabe .. " - " .. v .. " ist ein " .. k
end
_, ausgabe = ausgabe:match("(%s%-%s)(.+)") -- Unterstrich fängt nicht benötigten Rückgabewert ab
print("Ausgabe mit string.match(): " .. ausgabe)
ausgabe = "" -- Ausgabe zurücksetzen für eventuell weitere Durchläufe
-------------------------- Variante 2: Bindestrich entfernen mit der Funktion »string.sub()« --------------------------
local ausgabe = ""
local dichter = {
Klassiker = "Goethe",
Romantiker = "Tieck",
Realist ="Fontane"
}
for k, v in pairs(dichter) do
ausgabe = ausgabe .. " - " .. v .. " ist ein " .. k
end
ausgabe = ausgabe:sub(4) -- einen Substring ab dem 4. Zeichen generieren
print("Ausgabe mit string.sub(): " .. ausgabe)
ausgabe = "" -- Ausgabe zurücksetzen für eventuell weitere Durchläufe
Willst Du noch etwas üben:
Noch mehr Informationen zum Thema »Pattern Matching« gibt es unter riptutorial.com und fhug.org.uk und in diesem Video:
Mehr zum Thema »assoziative Tabellen« gibt es unter riptutorial.com und lua.lickert.net und in diesem Video:
AUFGABE FÜR MUTIGE: Kopiere den folgenden Quelltext in »ZeroBrane Studio«. Er enthält eine »assoziativen Tabelle«. Kannst Du erklären, was das Programm bewirkt und wie es funktioniert?
local function switch(a) if a then local switchcase = {} switchcase["string"] = tostring(a) .. " ist eine Zeichenkette." switchcase["number"] = tostring(a) .. " ist eine Zahl." switchcase["boolean"] = tostring(a) .. " ist ein Wahrheitswert (boolean)." return switchcase[type(a)] end return "Es wurde kein oder ein »falscher« Parameter übergeben." end local value = 5 print(switch(value)) value = "test" print(switch(value)) value = true print(switch(value)) print(switch())
Antwort – aber bitte überlege zunächst selber:
In vielen Programmiersprachen gibt es eine sogenannte switch-Anweisung (vgl. Wikipedia). »Lua« kennt dieses Konstrukt jedoch nicht. Der nachfolgende Quellcode ist der Versuch, eine solche switch-Anweisung mit einer assoziativen Tabelle nachzubauen.
Sieh Dir die Kommentare an, um die Funktionsweise nachzuvollziehen.
local function switch(a)
if a then -- prüfen, ob ein Parameter übergeben wurde
local switchcase = {} -- Tabelle anlegen, die die möglichen Fälle aufnimmt
-- Tabelle mit assoziativen Indizes für mögliche Fälle füllen
-- die Indizes heißen wie die Dateitypen, die geprüft werde sollen
-- tostring() wandelt übergebenen Parameter a in String um,
-- damit es keine Fehlermeldung bei der Zeichenverkettung mit .. gibt
switchcase["string"] = tostring(a) .. " ist eine Zeichenkette."
switchcase["number"] = tostring(a) .. " ist eine Zahl."
switchcase["boolean"] = tostring(a) .. " ist ein Wahrheitswert (boolean)."
-- Rückgabe, falls Parameter e i n e m assoziativen Index entspricht
return switchcase[type(a)]
end
-- Rückgabe, falls Parameter k e i n e m assoziativen Index entspricht
return "Es wurde kein oder ein »falscher« Parameter übergeben."
end
local value = 5
print(switch(value)) -- Aufruf der Funktion mit einer Zahl
value = "test"
print(switch(value)) -- Aufruf der Funktion mit einem String
value = true
print(switch(value)) -- Aufruf der Funktion mit einem boolean-Wert
print(switch()) -- Aufruf der Funktion mit keinem oder »falschem« Parameter