Bei Lua handelt es sich um eine in Brasilien Anfang der 90er-Jahre entwickelte Script-Sprache. Der Quelltext eines Lua-Programms wird vom Lua-Interpreter in Bytecode übersetzt und ausgeführt. Der Interpreter selbst ist in C geschrieben, was Lua-Programmen in der Ausführung zu hoher Performanz verhilft. Ferner ermöglicht das C-API es, Lua-Code in C/C++ Programmen einzubetten. Lua ist eine Multiparadigmensprache, die sich zum Schreiben von imperativem, funktionalem und objektorientiertem Code eignet.
Das stärkste Alleinstellungsmerkmal von Lua ist die einfache Einbettung in andere Systeme und Sprachen. Lua hat sich damit als „Glue Language“ etabliert und kommt in vielen Game-Engines zum Einsatz. Die Sprache lässt sich zudem zur Steuerung von Webservern wie Apache und nginx nutzen. Über die CGI-Schnittstelle wird Lua oft auch als eigenständige Internet-Programmiersprache verwendet. Ferner kommt die Sprache zur Programmierung mobiler Apps zum Einsatz.
Lua Scripting Tutorial: Die ersten Schritte
Der einfachste und schnellste Weg, mit Lua programmieren zu lernen, besteht darin, Lua-Code auf der interaktiven Lua-Demo-Seite auszuführen. Sie können alle im weiteren Verlauf des Artikels dargestellten Lua-Code-Beispiele testen. Kopieren Sie eines der Code-Beispiele in das Textfeld und klicken Sie auf „run“, um den Code auszuführen.
Auf diesem Weg sparen Sie sich die Installation. Sollten Sie Lua auf dem eigenen System nutzen wollen, folgen Sie unseren weiteren Anweisungen. Andernfalls gehen Sie gleich weiter zum Abschnitt „Die Grundlagen der Lua-Scriptsprache erlernen“.
Das eigene System für das Lua-Tutorial vorbereiten
Der Lua-Interpreter besteht aus einer einzigen Binärdatei, die auf der Kommandozeile nach dem Befehl „lua“ bereitsteht. Diese wird auf dem System abgelegt und muss ggf. in den Pfad aufgenommen werden. Ferner bietet Lua Bibliotheken, die die Einbettung von Lua-Code in C/C++ Programmen ermöglichen.
Für die Installation unter Mac und Linux bietet sich die Installation mit dem „Homebrew“-Paketmanager an. Wenn Sie auf Ihrem System Homebrew installiert haben, nutzen Sie am besten den folgenden Befehl auf der Kommandozeile, um Lua zu installieren:
Um Lua auf einem Windows-System zu installieren bietet sich der LuaDist-Installer an.
Den Lua-Interpreter interaktiv nutzen
Wie bei vielen Script-Sprachen üblich, lässt sich der Lua-Interpreter interaktiv ausführen. Im interaktiven Modus nimmt der Interpreter auf der Kommandozeile Lua-Code entgegen und führt diesen zeilenweise aus. Die dabei erzeugte Werte werden direkt auf der Kommandozeile ausgegeben. Als Nutzer kann man dann die Werte von Variablen überprüfen und ändern. Somit eignet sich dieser Ansatz besonders zum schnellen Prototypisieren. Um den Lua-Interpreter im interaktiven Modus zu starten, führen wir auf der Kommandozeile den folgenden Befehl aus:
# Lua-Interpreter im interaktiven Modus starten
lua -i
Hinweis
Um den interaktiven Modus wieder zu verlassen, geben Sie den Befehl „os.exit()“ ein, oder drücken Sie die Tasten [Strg]+[D] in Kombination.
Lua-Script für das Tutorial mit dem Lua-Interpreter ausführen
Anstatt Lua-Code auf der Kommandozeile Stück für Stück einzugeben, können Sie den Lua-Interpreter auch anweisen, eine komplette Lua-Quelltextdatei auszuführen. Dafür erzeugen wir zunächst eine Lua-Datei und übergeben dem Lua-Interpreter den Dateinamen zur Ausführung. Der Interpreter liest den in der Datei enthaltenen Quelltext Zeile für Zeile ab und führt diesen aus.
# Lua-Script ausführen
lua <dateiname>.lua
Hinweis
Lua-Quelltextdateien enden mit der Dateierweiterung „.lua“.
Lua-Script für das Tutorial mittels Hashbang direkt ausführbar machen
Auf den Betriebssystemen Linux / UNIX / macOS haben wir auch die Möglichkeit, eine Lua-Quelltextdatei direkt ausführbar zu machen. Dazu tragen wir einen so genannten „Hashbang“ als erste Zeile der Lua-Datei ein:
#!/usr/local/bin/lua
-- Lua Code für die Ausführung
Wie Sie sehen, enthält der Hashbang den Speicherort der Lua-Binärdatei, in unserem Fall ‚#!/usr/local/bin/lua‘. Unter Umständen weicht der Speicherort auf Ihrem lokalen System davon ab. In diesem Fall können Sie den Speicherort der Lua-Binärdatei ermitteln. Nutzen Sie dazu den „which“-Befehl auf der Kommandozeile:
# Speicherort der Lua-Binärdatei ermitteln
which lua
Nachdem Sie ein Lua-Script mit dem Hashbang versehen haben, müssen Sie die Datei als durch den Nutzer ausführbar markieren. Nutzen Sie dafür den folgenden Befehl auf der Kommandozeile:
# Lua-Datei als ausführbar markieren
chmod u+rx <dateiname>.lua
Führen Sie das Lua-Script dann im aktuellen Verzeichnis aus:
Tipp
Der Hashbang-Trick funktioniert auf Linux und UNIX-artigen Systemen wie macOS mit den meisten Script-Sprachen. Sie können nach dem selben Schema Ruby- oder Python-Scripte direkt ausführbar machen.
Die Grundlagen der Lua-Script-Sprache erlernen
Bei Lua handelt es sich um eine Multiparadigmensprache; der grundlegende Stil ist imperativ und funktional. Die Sprache ist komplett dynamisch, d.h. es wird keine Unterscheidung zwischen „compile time“ und „run time“ gemacht. Lua setzt ausnahmslos auf dynamische Speicherverwaltung: die Größe eines Objekts im Speicher kann sich während der Laufzeit ändern. Ein „Garbage Collector“ (GC) räumt nicht mehr benötigten Speicherplatz wieder frei, sodass der Programmierer sich nicht darum kümmern muss.
Kommentare in Lua-Scripten nutzen lernen
Kommentare sind essenzieller Bestandteil einer jeden Programmiersprache. Sie werden unter anderem für die folgenden Zwecke genutzt:
- Code-Bestandteile skizzieren
- Code-Features dokumentieren
- Code-Zeilen aktivieren / deaktivieren
Ein einzeiliger Kommentar in Lua beginnt mit einem doppelten Bindestrich (–) und läuft bis zum Ende der Zeile:
-- diese Zeile wird vom Interpreter ignoriert
In Kombination mit doppelten eckigen Klammern „[[“ und „]]“ lassen sich auch mehrzeilige Kommentare schreiben:
--[[
Dieser Kommentar
erstreckt sich über
mehrere Zeilen.
]]
Werte und Typen für das Lua-Scripting-Tutorial
Wie die meisten anderen Script-Sprachen ist auch Lua eine dynamisch typisierte Sprache. Typen gehören nicht zu Variablen, sondern zu Werten. Jeder Wert hat genau einen Typ, sei es Zahl, Zeichenfolge, Wahrheitswert etc. Insgesamt verfügt Lua über eine überschaubare Anzahl an Typen. Diese sind in der folgenden Übersicht zusammengefasst:
Typ | Erklärung |
number | Dezimalzahl |
string | Zeichenfolge |
boolean | Wahrheitswert: „true“ oder „false“ |
nil | Fehlender Wert; Typ hat nur den Wert „nil“ |
function | Funktion |
table | Zusammengesetzter Datentyp: Liste / Array, Hash / Dictionary |
thread | Coroutinen |
userdata | Benutzer-definierter C-Datentyp |
Lua kennt Literal-Syntax für Werte aller Typen mit Ausnahme von „thread“ und „userdata“. Um den Typ eines Werts zu ermitteln, nutzen wir die „type()“-Funktion. Diese gibt den Namen des Typs als String zurück. Hier ein paar Beispiele:
type(42) -- Typ ist `number`
type("Lua Tutorial") -- Typ ist `string`
type(false) -- Typ ist `boolean`
type(var) -- Typ ist `nil`, da `var` nicht definiert ist
Hinweis
Beachten Sie bei den folgenden Code-Beispielen, dass in Lua das erste Element einer Liste den Index 1 hat anstelle 0, wie in den meisten Sprachen üblich!
Mit Lua Programmieren lernen: Was sind Ausdrücke, Variablen und Operatoren?
Ein Ausdruck („Expression“) wird vom Interpreter evaluiert und liefert einen Wert zurück. Ausdrücke kombinieren Literale, Operatoren, Variablen und Funktionsaufrufe. Ausdrücke können optional mit Klammern „()“ gruppiert werden. Als dynamische Sprache ermittelt Lua den Typ des zurückgegebenen Werts automatisch. Hier einige Beispiele für Ausdrücke:
-- Arithmetische Operation in Lua
1 + 2 -- evaluiert zum Wert `3`
-- String-Konkatenation in Lua
'Walther' .. 'White' -- evaluiert zu `WaltherWhite`
-- String-Konkatenation mit automatischer Umwandlung einer Zahl in Lua
'Die ' .. 3 .. ' Musketiere' -- evaluiert zu `Die 3 Musketiere`
-- Gleichheits-Test in Lua
7 == '7' -- evaluiert zu `false`
-- Gleichheits-Test in Lua
'klein' == string.lower('KLEIN') -- evaluiert zu `true`
-- dynamische Typ-Information
type(var) == 'nil' -- evaluiert zu `true`, da `var` nicht definiert
Eine Variable ist ein Name für einen Wert im Speicher. Wie in den meisten Programmiersprachen beginnt ein Name in Lua mit einem Buchstaben oder Unterstrich (_), gefolgt von weiteren Buchstaben, Unterstrichen oder Zahlen. Dabei wird streng zwischen Groß- und Kleinschreibung unterschieden. Die folgenden reservierten Wörter dürfen für sich alleine nicht als Name verwendet werden:
„and“, „end“, „in“, „repeat“, „break“, „false“, „local“, „return“, „do“, „for“, „nil“, „then“, „else“, „function“, „not“, „true“, „elseif“, „if“, „or“, „until“, „while“
Allerdings können die reservierten Wörter problemlos als Teil eines Namens auftreten:
for = "Peter" -- ruft Fehler hervor
for_user = "Peter" -- erlaubt
Die Zuweisung eines Werts an eine Variable erfolgt über den Zuweisungs-Operator (=). Nicht zu verwechseln ist dieser mit dem logischen Gleichheits-Operator (==). Bei der Zuweisung wird wie üblich zwischen „L-value“ und „R-value“ unterschieden: die Variable muss auf der linken Seite des Zuweisungs-Operators stehen, ist also der L-value. Diesem wird der evaluierte Wert des rechts stehenden R-value zugewiesen:
anzahl = 13 -- das ist OK
13 = anzahl -- ruft Fehler hervor, da der Zahl 13 kein neuer Wert zugewiesen werden kann
Ein Operator erzeugt einen neuen Wert aus einem oder mehreren Operanden. Dabei spricht man von einem unären (einstelligen) oder binären (zweistelligen) Operator. Ein Operator verknüpft Operanden eines bestimmten Typs und liefert einen Wert eines bestimmten Typs zurück. Schauen wir uns die verschiedenen Operatoren in Lua an.
Arithmetische Operatoren operieren auf Zahlen und liefern eine Zahl zurück:
Arithmetische Operator | Arität | Operation |
+ | binär | Addition |
– | binär | Subtraktion |
* | binär | Multiplikation |
/ | binär | Division |
% | binär | Modulus |
^ | binär | Potenzierung |
– | unär | Negation |
Die relationalen Operatoren sind allesamt binär und testen, wie sich zwei Operanden zueinander verhalten. Sie liefern einen Wahrheitswert zurück:
Relationaler Operator | Test |
== | Gleichheit |
~= | Ungleichheit |
> | Größer als |
< | Kleiner als |
>= | Größer als oder gleich |
<= | Kleiner als oder gleich |
Logische Operatoren verknüpfen Wahrheitswerte und liefern einen neuen Wahrheitswert zurück:
Logischer Operator | Arität | Operation |
and | binär | UND-Verknüpfung |
or | binär | ODER-Verknüpfung |
not | unär | Negation |
Neben den genannten gibt es noch zwei spezielle Operatoren in Lua. Diese dienen zur Konkatenation von Strings (also ihrer Verkettung) sowie zur Ermittlung der Mächtigkeit eines zusammengesetzten Werts, wie Table oder String:
Operator | Arität | Operation |
.. | binär | String-Konkatenation |
# | unär | Anzahl Elemente eines Tables / Länge eines Strings ermitteln |
Lua kennt keine zusammengesetzten Zuweisungs-Operatoren wie „+=“ und „-=“, die in vielen Script-Sprachen üblich sind. Zum Inkrementieren und Dekrementieren von Variablen wird die Operation explizit ausgeschrieben:
preis = 42.99
rabatt = 0.15 -- 15% Rabatt
preis -= preis * rabatt -- `-=` funktioniert in Lua nicht
-- Dekrementierung muss stattdessen explizit geschrieben werden
preis = preis - (preis * rabatt)
Gültigkeitsbereiche und Blöcke für das Lua Script-Tutorial verstehen
Das Konzept des Gültigkeitsbereichs ist ein zentrales Konzept einer jeden Programmiersprache. Eine Variable existiert nur innerhalb eines bestimmten Gültigkeitsbereichs. Wie in JavaScript sind Variablen in Lua per Voreinstellung global. Jedoch ist die durchgehende Nutzung globaler Variablen als „Anti-Pattern“ bekannt und sollte vermieden werden. In Lua schafft das „local“-Schlüsselwort Abhilfe. Mit diesem wird der Gültigkeitsbereich einer Variable auf den umgebenden Block beschränkt – vergleichbar mit der Deklaration via „let“ in JavaScript.
-- diese Variable ist global
x = 5
-- lokale Variable definieren
local z = 10
Die Körper von Funktionen und Schleifen öffnen in Lua einen neuen Gültigkeitsbereich. Ferner nutzt Lua das Konzept des expliziten Blocks. Ein Block definiert einen neuen Gültigkeitsbereich für den zwischen den Schlüsselwörtern „do“ und „end“ stehenden Code. Dies entspricht den öffnenden/schließenden Klammern „{“ und „}“ in Java/C/C++. Das folgende Code-Beispiel verdeutlicht, wie Blöcke, Gültigkeitsbereiche und Variablen zusammenhängen:
-- äußerer Gültigkeitsbereich
do
local x = 1
do -- innerer Gültigkeitsbereich
local y = 2
-- `z` im globalen Gültigkeitsbereich erzeugen
-- dabei Zugriff auf lokale Variable `x` von äußerem Gültigkeitsbereich
-- sowie lokale Variable `y` vom inneren Gültigkeitsbereich
z = x + y -- `z` hat jetzt den Wert `3`
end
print(x) -- gibt `1` aus
print(y) -- gibt `nil` aus, da `y` im äußeren Gültigkeitsbereich nicht existiert
print(z) -- gibt `3` aus
end
-- `z` ist global, existiert also außerhalb des äußeren Gültigkeitsbereich
z = z + 4
print(z) -- gibt `7` aus
Mit den Lua-Kontrollstrukturen Programmieren lernen
Lua kennt die üblichen Kontrollstrukturen, die man auch in anderen Programmiersprachen findet. Dazu gehören die Verzweigungen und Schleifen. Hier ein Beispiel für Luas „if“-, „then“-, „else“-, „elseif“-Anweisungen:
limit = 42;
zahl = 43;
if zahl < limit then
print("Unter dem Limit.")
elseif zahl == limit then
print("Genau am Limit…")
else
print("Ãœber dem Limit!")
end
Neben der klassischen „while“-Schleife erkennt Lua auch deren Gegenstück „repeat“-„until“. Diese Anweisung findet man auch in Ruby. Sie erfordert die Umkehrung der eingesetzten Kondition. Das heißt, ein „while“ mit Kondition „zahl </= limit“ entspricht einem „repeat“-„until“ mit Kondition „zahl > limit“. Vorsicht mit der „repeat“-Anweisung! Unabhängig von der Kondition wird der Schleifenkörper mindestens einmal ausgeführt. Hier ein Beispiel:
limit = 10
zahl = 1
while zahl <= limit do
print("Zahl:", zahl)
zahl = zahl + 1
end
-- Achtung: obwohl `zahl` bereits größer als `limit`
-- wird der Schleifenkörper einmal ausgeführt
zahl = 11
repeat
print("Zahl:", zahl)
zahl = zahl + 1
until zahl > limit
So wie die meisten imperativen Programmiersprachen kennt auch Lua neben der „while“-Schleife eine „for“-Anweisung. Dabei kommen zwei Formen zum Einsatz: eine C-ähnliche Variante mit Schleifen-Variable sowie eine Variante mit Iterator. Betrachten wir zunächst die Nutzung der „for“-Anweisung mit Schleifen-Variable:
anfang = 1
ende = 10
for zahl = anfang, ende do
print("Aktuelle Zahl:", zahl) -- `1,2,3,4,5,6,7,8,9,10`
end
-- explizit Schritt auf `2` setzen
schritt = 2
for zahl = anfang, ende, schritt do
print("Aktuelle Zahl:", zahl) -- `1,3,5,7,9`
end
-- der Schritt kann auch negativ sein
schritt = -2
-- mit negativem Schritt Anfang und Ende vertauschen, um absteigend zu zählen
for zahl = ende, anfang, schritt do
print("Aktuelle Zahl:", zahl) -- `10,8,6,4,2`
end
Überraschenderweise ist die in der „for“-Schleife definierte Schleifen-Variable lokal, nicht global, ohne dass sie explizit als „local“ deklariert werden müsste. Das ist sinnvoll, und diesbezüglich unterscheidet sich Lua positiv von JavaScript. Dort ist eine ohne „let“ oder „var“ deklarierte Schleifen-Variable global, was zu schwerwiegenden Fehlern führen kann.
Schauen wir uns nun Luas „for“-Schleife mit Iterator an. Prinzipiell ähnelt der Ansatz dem von Python: Anstatt eine Schleifen-Variable zu inkrementieren und als Index in einer Liste zu nutzen, iterieren wir direkt über den Elementen der Liste. Zum Erzeugen des Iterators kommt häufig die „ipairs()“-Funktion zum Einsatz. Hier ein Beispiel:
-- Liste von Jahren definieren
dekaden = {1910, 1920, 1930, 1940, 1950, 1960, 1970, 1980, 1990}
-- mittels Iterator auf die einzelnen Jahre zugreifen
for index, jahr in ipairs(dekaden) do
print(index, jahr)
end
Mehr über Funktionen mit Lua lernen
Funktionen werden wie in C/C++, Java und JavaScript mit dem „function“-Schlüsselwort definiert. Wie üblich folgen die Funktionsparameter in Klammern nach den Funktionsnamen. Das Besondere bei Lua ist, dass beim Funktionsaufruf mit genau einem Literal als Argument die Klammern weggelassen werden dürfen. Eine Lua-Funktion muss nicht zwingend einen Wert zurückgeben. Der Definition nach handelt es sich ohne Wert um eine „Prozedur“:
-- Prozedur definieren
function hallo(name)
print("Guten Tag", name)
end
-- Funktion aufrufen
hallo("der feine Herr")
-- auch dies ist möglich
hallo "der feine Herr"
-- folgendes funktioniert jedoch nicht
name = "Walther"
hallo name -- Syntax-Fehler
-- mit Variable statt Literal muss die Funktion mit Klammern aufgerufen werden
hallo(name)
Will man aus einer Funktion einen Wert zurückzugeben, kommt wie üblich das „return“-Schlüsselwort zum Einsatz. Dieses beendet die Ausführung der Funktion und gibt den angegebenen Wert zurück. In folgendem Code-Beispiel wird eine Zahl quadriert:
-- Funktion mit einzelnem Rückgabewert
function quadrat(zahl)
-- der Ausdruck `zahl * zahl` wird evaluiert
-- und sein Wert zurückgegeben
return zahl * zahl
end
-- Zahl quadrieren
print(quadrat(9)) -- `81`
Wie in Python und JavaScript kann eine Funktion in Lua eine variable Anzahl von Argumenten entgegennehmen. Die Argumente werden im speziellen Konstrukt „(…)“ gespeichert. Um auf die Argumente zuzugreifen, ist es oft nützlich, diese mit dem Ausdruck „{…}“ in einer Liste zusammenzufassen. Alternativ kommt die „select()“-Funktion zum Einsatz, die ein Argument unter dem angegebenen Index extrahiert. Die Anzahl der Argumente ermitteln wir mit dem Ausdruck „#{…}“.
-- alle Argumente einer Funktion drucken
function var_args(...)
for index, arg in ipairs({...}) do
print(index, arg)
end
end
var_args('Peter', 42, true)
Neben der variablen Anzahl von Argumenten erlaubt Lua auch, mehrere Werte mit einer „return“-Anweisung zurückzugeben. Dies funktioniert ähnlich wie in Python, jedoch ohne expliziten „Tupel“-Typ. Wie in Python ist es üblich, die Rückgabewerte beim Funktionsaufruf mehreren Variablen zuzuweisen. Hier ein Beispiel:
-- Funktion mit mehreren Rückgabewerten
function erster_und_letzter(liste)
-- erstes und letztes Element der Liste zurückgeben
-- einzelne Rückgabewerte werden durch ein Komma `,` getrennt
return liste[1], liste[#liste]
end
leute = {"Jim", "Jack", "John"}
-- Zuweisung der Rückgabewerte an mehrere Variablen
erster, letzter = erster_und_letzter(leute)
print("Der erste ist", erster)
print("Der letzte ist", letzter)
Wird einer der Rückgabewerte nicht benötigt, nutzt man der gängigen Konvention folgend den Unterstrich (_) als Platzhalter, wie im folgenden Beispiel dargestellt:
function min_mittel_max(...)
-- Anfangswerte für `min` und `max` auf erstes Argument setzen
local min = select(1, ...)
local max = select(1, ...)
-- Mittelwert anfangs auf null setzen
local mittel = 0
-- über den Zahlen iterieren
-- die Index-Variable benötigen wir nicht
-- und nutzen daher `_` als Platzhalter
for _, zahl in ipairs({...}) do
-- ggf. neues Minimum setzen
if min > zahl then
min = zahl
end
-- ggf. neues Maxiumum setzen
if max < zahl then
max = zahl
end
-- für den Durchschnitt Zahlen summieren
mittel = mittel + zahl
end
-- Summe der Zahlen durch deren Anzahl teilen
mittel = mittel / #{...}
return min, mittel, max
end
-- hier benötigen wir den `mittel`-Wert nicht
-- und nutzen daher `_` als Platzhalter
min, _, max = min_mittel_max(78, 34, 91, 7, 28)
print("Minimum und Maximum der Zahlen sind", min, max)
Funktionen sind in Lua „first-class citizens“. Das heißt, sie können an Variablen gebunden werden und lassen sich damit auch als Argumente an andere Funktionen übergeben. Ferner kann eine Funktion als Rückgabewert einer Funktion dienen. Zusammengenommen ermöglicht Lua damit funktionale Programmierung, hier dargestellt am Beispiel der berühmten „map()“-Funktion:
-- `map()`-Funktion in Lua
-- nimmt eine Funktion `f` und eine Liste als Argumente entgegen
function map(f, liste)
-- neue Liste für Ausgabewerte anlegen
local _liste = {}
-- über den Elementen der Liste mit Index iterieren
for index, wert in ipairs(liste) do
-- Funktion `f()` auf den aktuellen Wert der Liste anwenden
-- und den Rückgabewert in neuer Liste am selben Index speichern
_liste[index] = f(wert)
end
-- neue Liste zurückgeben
return _liste
end
-- Liste von Zahlen
zahlen = {3, 4, 5}
-- Funktion, die auf alle Elemente der Liste angewandt wird
function quadrat(zahl)
return zahl * zahl
end
-- erzeugen der Quadrate via `map()`-Funktion
quadrate = map(quadrat, zahlen) -- `{9, 16, 25}`
-- quadrierte Zahlen ausgeben
for _, zahl in ipairs(quadrate) do
print(zahl)
end
In der funktionalen Programmierung wird oft Rekursion genutzt: Eine Funktion ruft sich selber mit veränderten Argumenten immer wieder auf. Dabei müssen wir bei Lua besondere Vorsicht walten lassen. Funktionen, die rekursiv aufgerufen werden, sollte man explizit als „local“ deklarieren.
function f()
-- rekursiver Aufruf
f() -- verweist ggf. auf globale Variable `f`
end
-- stattdessen
local function f()
-- rekursiver Aufruf
f() -- verweist auf die umgebende Funktion
end
-- equivalent zu
local f; -- Variable `f` explizit als `local` deklarieren
f = function() -- Zuweisung der Funktion an die lokale Variable `f`
f() -- verweist garantiert auf die umgebende Funktion
end