019 Übung lua iI:
Von Fall zu Fall: if
wenn vergleichsWert == wahr dann führe einen Befehl aus endeVergleich
Wert 1 == Wert 2 -- Vergleich, ob Wert1 gleich Wert 2 ist Wert 1 < Wert 2 -- Vergleich, ob Wert 1 kleiner ist als Wert 2 Wert 1 > Wert 2 -- Vergleich, ob Wert 1 größer ist als Wert 2 Wert 1 <= Wert 2 -- Vergleich, ob Wert 1 kleiner oder gleich ist als Wert 2 Wert 1 >= Wert 2 -- Vergleich, ob Wert 1 größer oder gleich ist als Wert 2 Wert 1 ~= Wert 2 -- Vergleich, ob Wert 1 ungleich Wert 2 not (Vergleich) -- kehrt das Ergebnis des Vergleichs um
local a = 5 if a == 5 then print("Der Wert der Variablen a ist gleich: " .. a) end
AUFGABE: Schreibe Codezeilen, die die obige Variable a mit verschiedenen »Vergleichsoperatoren« verwendet und jeweils passende Ausgaben mit print() macht.
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local a = 5
if a > 4 then
print("Variable a hat den Wert: " .. a)
end
if a <= 5 then
print("Variable a ist kleiner oder gleich 5.")
end
AUFGABE: Kannst Du statt einzelner Variablen die folgende Tabelle verwenden? Sie soll mit einer for-Schleife durchlaufen werden und nur dann eine print()-Ausgabe machen, falls der gefundene Zahlenwert größer als 5 ist:
local my_table = { 3, 10, 7 }
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local my_table = {
3,
10,
7
}
for i, v in ipairs(my_table) do
if my_table[i] > 5 then
print(my_table[i] .. " ist größer als 5.")
end
end
Abfragen verknüpfen:
local a = 5 local b = 3 if a == 8 or b > 2 then print("Einer der Vergleiche mit »or« wurde als wahr erkannt") end
Statt: if a == 8 or b > 2 then Besser: if (a == 8) or (b > 2) then
AUFGABE: Schreibe Codezeilen für eine lokale Variable a mit dem Zahlenwert 5. Eine Ausgabe mit print() soll nur erfolgen, wenn der Datentyp (type) auf number lautet und a größer als 3 ist. Den Befehl type setzt Du so ein:
type(a) == "number"
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local a = 5
if (type(a) == "number") and (a > 3) then
print("Die Variable a ist eine Zahl mit dem Wert " .. a)
end
a = "Hallo"
if (type(a) == "string") and (a == "Hallo") then
print("Die Variable a ist eine Zeichenkette und lautet: " .. a)
end
Alternative Wege und verschachtelte Abfragen:
wenn vergleichsWert == wahr dann führe diesen Befehls-Block aus ansonsten führe diese alternativen Befehls-Block aus endeVergleich
local a = 5 if a == 5 then print("Die Variable a hat den Wert 5.") else print("Die Variable a hat einen Wert ungleich 5.") end
local a = 5 if a == 5 then print("Die Variable a hat den Wert 5.") elseif a > 5 then print("Die Variable a hat einen Wert größer 5.") elseif a < 5 then print("Die Variable a hat einen Wert kleiner 5.") else print("Die Variable a hat einen Wert ungleich 5.") end
Was also tun in unserem konkreten Fall? Wir verschachteln unsere Abfrage (»nested if«) und ummanteln sie mit einer Abfrage auf den Datentyp. Hast Du eine Idee, wie das aussehen könnte?
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local a = 5
if type(a) == "number" then
if a == 5 then
print("Die Variable a hat den Wert 5.")
elseif a > 5 then
print("Die Variable a hat einen Wert größer 5.")
else
print("Die Variable a hat einen Wert kleiner 5.")
end
else
print("Die Variable a ist keine Zahl, sondern vom Datentyp »" .. type(a) .. "«.")
end
local a = 5 if a then print("if-Zweig: Werde ich angezeigt? Falls ja: warum?") else print("else-Zweig: Werde ich angezeigt? Falls ja: warum?") end
Antwort – aber bitte überlege zuerst selber:
Es wird der if-Zweig der Abfrage ausgedruckt.
Die Schreibweise »if a then« ist eine Verkürzung der Abfrage »if a ~= nil then«. Der Ausdruck »nil« steht in »Lua« für die Abwesenheit eines gültigen abfragbaren Wertes. Da die Variable a aber ja zu Beginn mit »local a = 5« einen Wert zugewiesen bekommen hat, ist a ungleich »nil«, der if-Zweig also wahr.
Man kann »nil« auch nutzen, um eine Variable zu löschen. Mit der Zuweisung »a = nil« hätten wir die Variable a zerstört. Man kann nicht mehr auf sie zugreifen, bis sie irgendwo im Skript mit »local a = wertzuweisung« wieder neu angelegt wird.
local a = 3 if not (a == 1) then print("a ist nicht 1") end
Exkurs: Sichtbarkeit von Variablen:
do local x = 5 print(x) -- Ausgabe: 5 end print(x) -- Ausgabe: nil
Im Zusammenhang mit dem scope-Konzept steht das »variable shadowing«. Dies bedeutet, dass zwei Variablen in verschiedenen »scopes« den selben Namen tragen dürfen, ohne sich dadurch gegenseitig zu beeinflussen. Zum Tragen kommt dann immer die Variable innerhalb des »scopes«, der gerade ausgeführt wird:
x = 10 -- Achtung: dies ist eine globale Variable do -- wir definieren einen neuen Geltungsbereich local x = x -- lokaler Variable »x« wird Wert der globalen Variable »x« zugewiesen print(x) -- Ausgabe der lokalen Variable ergibt »10« x = x + 1 -- die lokale Variable »x« um den Wert 1 »inkrementieren« print(x) -- Ausgabe der lokalen Variable »x« ergibt »11« end print(x) -- es wird die globale Variable »x« mit dem Wert »10« ausgegeben -- da die lokale Variable »x« außerhalb des »chunks« nicht erreichbar ist
Codezeilen auslagern: Funktionen
Richtig. So haben wir die Übungen zum Thema for-Schleife eingeleitet. Jetzt lernst Du eine weitere Möglichkeit kennen, Programmabschnitte (»chunks«) in einem Skript so zu strukturieren, dass immer wiederkehrende Aufgaben nicht jedes Mal von Grund auf neu geschrieben werden müssen. Kurzum: Wir wollen uns mit »Funktionen« beschäftigen.
Als Pseudocode könnte eine »Funktion« so aussehen:
Funktion Name_Der_Funktion(Übergabewerte) auszuführende Befehle Ende der Funktion Funktionsaufruf Name_Der_Funktion(Übergabewerte)
Ein entsprechender Quellcode in »Lua« mit konkreten Befehlen könnte so aussehen:
local my_printer = function(s_value, e_value, p_string) local start_value = s_value local end_value = e_value local print_string = p_string for i = start_value, end_value do print(print_string .. i) end end my_printer(1, 10, "Index: ")
Es gibt übrigens verschiedene Schreibweisen, eine »Funktion« in »Lua« anzulegen. Erlaubt wäre zum Beispiel auch:
local function my_printer(s_value, e_value, p_string)
local my_printer = function(s_value, e_value, p_string)
Der nun folgende Funktionskörper enthält dann die Befehle, die ausgeführt werden sollen. In unserem Beispiel werden zu Beginn die formalen Parameter drei lokalen Variablen zugewiesen:
local start_value = s_value local end_value = e_value local print_string = p_string
Wichtig: Jede »Funktion« muss mit »end« abgeschlossen werden.
Ist die »Funktion« einmal geschrieben, kann sie beliebig oft im Skript mit verschiedenen Parametern aufgerufen werden:
my_printer(1, 10, "Index: ") my_printer(2, 100, "Ausgabe: ") my_printer(3, 60, "Wert: ")
Zum Vergleich: Quellcode ohne die Funktion »my_printer«
local start_value = 1
local end_value = 10
local print_string = "Index: "
for i = start_value, end_value do
print(print_string .. i)
end
start_value = 2
end_value = 100
print_string = "Ausgabe: "
for i = start_value, end_value do
print(print_string .. i)
end
start_value = 3
end_value = 60
print_string = "Wert: "
for i = start_value, end_value do
print(print_string .. i)
end
Doch unsere »Funktion« hat noch einige kleine Schönheitsmakel. Erkennst Du welche?
Antwort – aber bitte überlege zuerst selber:
Die übergebenen Paramter für »s_value« und »e_value« müssen Zahlen, also vom Datentyp number sein, sonst funktioniert die for-Schleife nicht und das Programm bricht mit einer Fehlermeldung ab.
Und was passiert, wenn die Funktion mit zu wenig oder gar keinen Parametern aufgerufen wird, also beispielsweise in dieser Form: my_printer()
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local my_printer = function(s_value, e_value, p_string)
local start_value = s_value
local end_value = e_value
local print_string = p_string
if type(start_value) ~= "number" then
start_value = 1
end
if type(end_value) ~= "number" then
end_value = 10
end
if type(print_string) ~= "string" then
print_string = "Index: "
end
for i = start_value, end_value do
print(print_string .. i)
end
end
my_printer(x, y, 3)
Exkurs Minetest:
minetest.register_node("mode_name:node_name", { -- weitere Befehle -- })
register_node(erster Parameter_als_String, zweiter Parameter_als_Tabelle)
Noch ein Beispiel gefällig? Als wir in der Übung »015 DEPENDS« unsere Töne aus dem Standard-Mod »default» bezogen haben, kam ebenfalls eine »Funktion« zum Einsatz:
default.node_sound_metal_defaults(Parameter_als_Tabelle)
Antwort – aber bitte überlege zuerst selber:
print(Parameter_als_Variable oder direkt string in Anführungszeichen)
type(Parameter_als_Variable oder direkt string, number etc.)
ipairs(Parameter_als_Tabelle)
io.read() -- keine Parameter bzw. Parameter durch die Tastatureingabe
Funktionen mit Rückgabewerten:
Funktion Name_Der_Funktion(Übergabewerte) auszuführende Befehle und Berechnungen Rückgabe des Ergebnisses an das laufende Skript Ende der Funktion Ergebnis_Variable = Funktionsaufruf Name_Der_Funktion(Übergabewerte)
local return_value = "" local my_calculator = function(f_value, s_value) local first_value = f_value local second_value = s_value return first_value + second_value end return_value = my_calculator(1, 3) print(return_value)
local my_misplaced_return = function(value_one, value_two) return value_one + value_two print("Die Summe wurde erfolgreich berechnet") end my_misplaced_return(1, 3)
return_value = my_calculator(1, 3)
print(my_calculator(1, 3))
AUFGABE: Kannst Du den obigen Quellcode so ergänzen, dass keine Fehlermeldung auftritt, falls jemand beim Funktionsaufruf einen falschen Datentyp oder gar keine Parameter übergibt?
AUFGABE: Sieh Dir den nachfolgenden Quelltext an. Wann wird das erste return ausgeführt, wann das zweite? Wann werden beide return-Anweisungen ausgegeben?
local output = "" local my_returner = function(value) if type(value) == "string" then return "Es wurde ein »string« als Datentyp übergeben." end return "Alle Daten wurden efolgreich verarbeitet." end output = my_returner("Hallo Welt") print(output) output = my_returner(3) print(output)
Antwort – aber bitte überlege zuerst selber:
Sobald eine return-Anweisungen ausgeführt wurde, ist der Funktionsaufruf beendet. Alle weiteren nach diesem return noch im Funktionskörper befindlichen Befehle werden ignoriert.
In unserem Beispiel bedeutet dies, dass immer nur einer der beiden returns zur Ausführung gelangt, je nachdem welcher Parametertyp übergeben wurden. Ist es ein string, lautet der Rückgabewert »Es wurde ein ›string‹ als Datentyp übergeben.«. In allen anderen Fällen wird immer das zweite return verwendet. Es können jedoch nie die Angaben beider returns ausgelesen werden.
local return_type = "" local data_type = function(value) if type(value) == "number" then return "Übergebener Datentyp ist »" .. type(value) .. "«" elseif type(value) == "string" then return "Übergebener Datentyp ist »" .. type(value) .. "«" else return "Übergebener Datentyp ist unbekannt oder nil" end end return_type = data_type(10) print(return_type)
Lösung – aber bitte probiere zuerst selber einen Quellcode zu schreiben:
local return_type = ""
local data_type = function(value)
local tmp = ""
if type(value) == "number" then
tmp = "Übergebener Datentyp ist »" .. type(value) .. "«"
elseif type(value) == "string" then
tmp = "Übergebener Datentyp ist »" .. type(value) .. "«"
else
tmp = "Übergebener Datentyp ist unbekannt oder nil"
end
return tmp
end
return_type = data_type(10)
print(return_type)