Das Lua-Tutorial - 1 Grundlagen

  • Diese Artikelserie zeigt, wie man mit Lua eigene Funktionen in das Programm einbinden kann. „#1 – Grundlagen“ erklärt das Fundament von Lua und wie man damit arbeitet. Es ist spezifisch für RailWorks ausgelegt.

    1 Das Lua-Tutorial - 1 Grundlagen

    1.1 Was ist Lua und was benötige ich?


    PP_LUATUT01_01_LuaEdit_3.03RC.png


    Lua ist eine Formsprache, mit der man ein Programm von außen steuern kann. Was heißt „außen“ und wie ist das gemeint?
    Ein Programm besitzt einen inneren Kern, der immer gleich abläuft – er ist fest verankert und damit von „innen“ gesteuert.Wir können ihn mit normalen Mitteln nicht verändern. Lässt ein Programm zu, dass es veränderbar ist,dann lässt es eine Steuerung von außen zu. Das kann mit einer Konfigurationsdatei („Bildschirmauflösung einstellen“)oder einem Script gelöst sein. Das Script sagt was der Kern erledigen soll.Und diese Scripte werden zum Beispiel mit Lua geschrieben – darum geht es hier.
    Um solche Scripte zu schreiben, muss man einer Formsprache exakt folgen. Das ist wie Tangotanzen auf der Showbühne: Nur das unser Tanzpartner die Tastatur ist. Die Formsprache beschreibt uns die Regeln, wie etwas zu formulieren ist, welche Möglichkeiten gegeben sind und was dadurch ausgelöst wird.
    Natürlich passiert es schnell, dass mal nicht alles glatt läuft. Und zum Glück gibt es Programme, die uns unterstützen, Fehler durch korrekten Text zu ersetzen. Diesen Text den wir schreiben, nennt man Quellcode.
    Ein Programm, das ich sehr gerne nutze und empfehle, ist LuaEdit 3.03RC. Es ist einfach organisiert und ermöglicht eine schnelle Überprüfung der Formsprache. So sieht das Programm aus, wenn man Scripte schreibt (siehe rechts).
    Die Entwicklungsumgebung LuaEdit 3.03RC
    Oben haben wir ein Menü. Es ermöglicht allgemeine Operationen, Sichteinstellungen, Projektverwaltung, einen Test durchzuführen und das starten von Tools.
    In der oberen Toolleiste finden wir in der ersten Gruppe Felder zum erzeugen neuer Dateien oder Projekte.In der zweiten Gruppe können wir Dateien öffnen und speichern. In der dritten Gruppe können wir Dateien zu Projekten zuordnen. Die vierte Gruppe ermöglicht das Drucken. Die fünfte Gruppe kannden Testdurchlauf beeinflussen. Die sechste Gruppe ermöglicht das Prüfen der Syntax unddas Erstellen von kompilierten Scripten. Die siebente Gruppe ermöglicht das Suchen und das Setzen von Haltepunkten im Script, wenn wir einen Testdurchlauf durchführen.
    Die untere Toolleiste vereinfacht das Bearbeiten von Text. Dazu zählt zum Beispieldas automatische Ein- und Auskommentieren oder Verrücken von Text.Am besten probiert man an einem Testtext, was der jeweiligen Schalter bewirkt :-).
    Ich habe bei der Beschreibung das Wort „Kompilieren“ in den Raum geworfen.Das ganze muss man sich so vorstellen: Wir schreiben Text in lesbarer Form,den wir verändern können. Kompilieren wir den Text, wird unser geschriebenesinterpretiert und mit programmeigenen Anweisungen übersetzt. Die programmeigenenAnweisungen können wir natürlich nicht ohne weiteres frei bearbeiten.Das ganze macht man, damit man Zeit spart, weil die Interpretation manchmalganz schön lange dauern kann, wenn man sehr viele Scriptdateien hat.Aber damit müssen wir uns in RailWorks nicht herumärgern: Das macht RailWorks für uns, leider.


    1.2 Kommentieren hilft


    Scripten ist eine Gemeinschaftsaufgabe. Schreiben wir Quellcode, müssen wir später verstehen,was wir geschrieben haben. Und unseren Mitgliedern hilft es, Fehler schneller und leichter zu entlarven,um sie auszumerzen. Ein Kommentar ist dabei etwas, was nicht vom Interpreter (dem „Leser“) beachtet wird.
    Er wird mit zwei arabischen Zahlstrichen angefangen (--) und gilt für die eine Zeile. Etwa so:
    -- Kommentare sind für folgende Fälle gedacht:

    • Hinweise am Anfang der Dateien

      • wer, was, wann und wie etwas geändert hat.
      • Falls notwendig: Nenne

        • die verwendete Sprache
        • den Namen der Gruppe
        • oder die Lizenz (und/oder deren Bedingungen).
    • Eigenschaften der Funktionen

      • Was sie machen sollen oder bewirken
      • Wann sie verwendet werden dürfen
      • Welche Parameter zulässig sind und was sie bedeuten – die Einheiten
      • Rückgabewerte, wenn Sie vorhanden sind
    • Beschreibung der Aufgabenfelder

      • was wie erledigt werden soll
      • welche Werte warum besondere Rollen spielen.

    Es ist ein logischer Prozess und wird eine Menge Schreibarbeit verursachen.
    Aber das Tutorial ist so ausgelegt, dass die „Kommunikationsaufgabe Kommentieren“ als Routine ein leichtes Spiel wird.


    1.3 Variablen und Werte


    Informationen müssen gespeichert werden: Dieser Aufgabe werden Variablen gerecht.
    Ihre Werte werden durch logische Berechnungsprozesse oder festgelegte Logik bestimmt.
    Damit man unterscheiden kann, was Werte beinhalten, unterteilt man diese in Dateitypen.
    Für uns wichtig kann sein:


    Datentyp Beschreibung Beispiel

    string Ein Text aus einer Verkettung von Zeichen. „Hallo Welt“
    number Eine Zahl. Bei Gleitkommazahlen schreibt man mit Punkt statt mit Komma.
    Ein „f“ kann zusätzlich stehen und hat für uns keine Bedeutung.
    3, 5, 9
    2.6, 3.1415f
    Boolean Ein Wahrheitswert. Wahr oder Falsch. Wert: true oder false
    nil Eine Variable, die keinen Wert hat: Datentyplos. Wert: nil
    table Eine Tabelle: „Array“. Definition: { }



    Der Datentyp wird zur Laufzeit gewählt und ist bei vielen Typen austauschbar.
    So kann ein string zum Beispiel zu einer Zahl umgewandelt werden, wenn dies möglich ist.
    Ein Wahrheitswert, eine Zahl oder auch nil kann zu einem Text umgewandelt werden.
    Die Definition einer Variable ist einfach – man schreibt sie in folgender Form auf:
    [Name] = [Wert]


    Dazu ein paar Beispiele:
    PP_LUATUT01_02_Variablendefinitionen.png


    Ganz links ist die Zeilennummer, Einzustellen in den Optionen von LuaEdit.Die Namensgebung von Variablen entspricht den obigen Beispielen:
    Sie darf mit einem Buchstaben anfangen, der keine Nummer ist. Nach dem ersten Buchstaben darf man Nummern verwenden – das meidet man allerdings.


    1.4 Operatoren


    Beim Programmieren unterscheidet man in drei Arten von Operationen:

    • Arithmetische Operationen (Rechenoperationen)
    • Relationale Operationen (Vergleichsoperationen) und
    • Logische Operationen

    Jede Gruppe deckt bestimmte Aufgabenfelder ab. So dienen arithmetische Operationen
    der Berechnung von Werten und relationale und logische Operationen dem Vergleich von Werten.
    Dieser Abschnitt beschäftigt sich mit Rechenoperationen. Das sind die Operatoren

    • "=" --> Zuweisungsoperator
    • "+" --> Additionsoparator
    • "-" --> Subtraktionsoperator oder auch Negationsoperator
    • "*" --> Multiplikationsoperator
    • "/" --> Divisionsoperator
    • ".." --> Verknüpfungsoperator

    Um diese Funktion zu verdeutlichen, dienen folgende Beispiele:
    PP_LUATUT01_03_ArOperationen.png


    1.5 Kontrollstrukturen und Schleifen

    1.5.1 Anweisungen und Rümpfe


    Jede Anweisung wird in einem Rumpf ausgeführt. Dabei unterscheidet man in

    • Anweisungsrümpfe
    • Branchrümpfe
    • Branchrümpfe der If-Else-Kontrollstrukturen

    Branchrümpfe begrenzen einen Programmblock und benötigen eine Bedingung:
    (Bedingung) then
    (Anweisung)
    ]end


    Sie werden mit then eingeleitet und mit end beendet.
    Anweisungsrümpfe der If-Else-Kontrollstrukturen werden wie Anweisungsrümpfe definiert,
    können jedoch ohne das Schlüsselwort end beendet werden, wenn sie mit elseif oder else beendet werden.
    if (Bedingung) then
    (Anweisung)
    elseif (Bedingung) ...
    Anweisungen wie a = 2 können mit einem Semikolon angeschlossen werden,
    wie es in anderen Programmiersprachen üblich ist: a = 2;


    1.5.2 Wenn-Kontrollstrukturen


    Im vorigen Abschnitt wurden logische Operatoren genannt – diese sind Bestandteil der Kontrollstrukturen und Schleifen.
    Unter Kontrollstrukturen kann man sich einfache Wenn-Verzweigungen vorstellen.
    Schleifen werden hingegen solange ausgeführt, wie die Bedingung zutrifft.
    Eine Verzweigung wird mit einer if-Bedingung realisiert:
    if (Bedingung) then
    (Anweisungen)
    end[



    Die Bedingungen werden mit Operatoren geprüft oder verknüpft. Die Vergleichsoperationen sind:


    Operator Bedeutung Beispiel
    == Gleichheitsoperator. Wahr wenn beide Werte gleich sind. 5 == 5 --> Wahr
    ~= Ungleichheitsoperator. Wahr wenn beide Werte nicht gleich sind. 5 ~= 5 --> Falsch
    < Kleiner-als-Operator. Wahr wenn der zu prüfende Wert kleiner ist als der andere. 4 < 5 --> Wahr
    <= Kleiner-als-oder-gleich-Operator. Wahr wenn der zu prüfende Wert kleiner oder gleich dem anderen ist. 5 <= 5 --> Wahr
    > Größer-als-Operator. Wahr wenn der zu prüfende Wert größer ist als der andere. 6 > 5 --> Wahr
    >= Größer-als-oder-gleich-Operator. Wahr wenn der zu prüfende Wert größer oder gleich dem Anderen ist. 5 >= 5 --> Wahr


    Vergleichsoperationen können verknüpft werden. Dazu dienen folgende Vergleichsoperationen:


    Operator Bedeutung Beispiel
    and Und-Operator. Gibt wahr zurück, wenn beide Operanden wahr sind. true and true --> Wahr
    true and false --> Falsch
    or Oder-Operator. Gibt wahr zurück, wenn ein Operand wahr ist. false or false --> Falsch
    true or false --> Wahr
    not Wahrheits-Negations-Operator. Mach ein Wahr zu Falsch und ein Falsch zu wahr. not true wird zu false
    not false wird zu true



    Zusatzbemerkung: not ist vorrangig zu den arithmetischen Operatoren.
    Logikoperationen zu Vergleichsoperationen.
    Arithmetische Operationen vorrangig zu Logikoperationen.
    Zu Wahrheitswerten ist kein Vergleichsoperator notwendig – er wird immer gegen true geprüft.
    Oftmals ist es notwendig, weitere Fall-Abzweige zu programmieren.
    Vom Prinzip: wenn das… oder wenn das… oder das. Dies wird wird else-if-Kontrollstrukturen realisiert.
    Der Aufbau dieser Kontrollstrukturen ist der Folgende:
    if (Bedingung) then
    (Anweisungsblock den Fall)
    else
    (Anweisungsblock für unbehandelte Fälle)
    end


    if (Bedingung A) then
    (Anweisungsblock für Fall A)
    elseif (Bedingung B) then
    (Anweisungsblock für Fall B)
    (optional: else
    (Anweisungsblock für unbehandelte Fälle))
    end


    Um das ganze System zu verdeutlichen, dienen folgende Beispiele:
    PP_LUATUT01_06_Vergleiche.png


    1.5.3 Schleifen


    Schleifen dienen zur Wiederholung einer Anweisung, solange eine bestimmte Bedingung gültig ist.
    Die einfachste Schleife ihrer Art ist die while-Schleife. Solange die Bedingung gültig ist,
    wird deren Anweisung im then-end-Block ausgeführt:
    while (Bedingung) do
    (Anweisung)
    end



    Das Folgende Beispiel lässt den Wert c solange herunterzählen, bis c Null ist:
    PP_LUATUT01_07_While-Schleife.png


    Dekrementieren = Zahl um 1 verringern. Inkrementieren = Zahl um 1 erhöhen.
    Manchmal ist es Notwendig, dass der Anweisungsblock vor der Bedingungsprüfung notwendig ist.
    Dazu dienen repeat-until-Schleifen – auch bekannt als do-while-Schleife.
    repeat
    (Anweisungen)
    until (Bedingung)


    PP_LUATUT01_09_Repeat-Until-Schleife.png


    Eine weitere interessante Schleife ist die numerische for-Schleife.
    In ihr ist die Deklaration, Zielwert und Schrittweite definiert.
    Folgende Form ist dabei zu beachten:
    for (Deklaration), (Zielwert)(optional:,(Schrittweite)) do
    (Anweisung)
    end



    Ist die Schrittweite nicht definiert (weil optional), wird 1 als Standardwert verwendet.
    Das folgende Beispiel berechnet c von 0 bis 10 als Folge:
    PP_LUATUT01_08_For-Schleife.png


    1.6 Funktionen


    Funktionen werden wie folgt aufgerufen:
    Funktionsname ( Argument, Argument, ... )


    Beispiel:
    SendSignalMessage(PZB_500, ““, 1, 1, linkIndex)


    Möchte man eigene Funktionen programmieren, verwendet man folgende Form:
    function Funktionsname(Parameter, Parameter, ...)
    (Anweisungen)
    end



    Es sei noch zu erwähnen, dass man an Funktionen Argumente (Werte) übergibt und bei der Definition Parameter definiert.
    Folglich nennt man die übergebenen Argumente innerhalb einer Funktion Argument.
    Das folgende Beispiel zeigt die Deklaration der Funktion DebugMessage() und deren Aufruf.
    PP_LUATUT01_10_Funktionen.png


    Möchte man Rückgabewerte zurückgeben, wird dies mit dem Schlüsselwort return  eingeleitet.
    In Lua ist es möglich mehrere Rückgabewerte zurückzuliefern.
    Das folgende Beispiel zeigt, wie das geht:


    PP_LUATUT01_11_R%C3%BCckgabewerte.png


    1.7 Tabellen


    Fast geschafft. Im letzten „Lernkapitel“ werden Tabellen behandelt. Tabellen werden auch als Arrays bezeichnet.
    Sie beinhalten Daten in Form von Schlüssel-Wert-Paaren.
    Eine Tabelle wird wie folgend deklariert:
    Variable = {}


    Es ist ebenfalls möglich, zur Deklaration Werte in Tabellen schreiben:
    Variable = { VariablenName = Wert, ... }
    Der Zugriff ist vielseitig. Entweder greift man über eckige Klammern auf ein Schlüssel-Wert-Paar zu.
    Der Zugriff funktioniert auch über ein Tabelle-Punkt-Variablenname-System:
    PP_LUATUT01_12_Tabellen.png


    Eine weitere Schleifenart ist die generische for-Schleife. Sie wird für Tabellen benutzt.
    Um sie zu realisieren benutzt man die Funktion inpairs(Tabelle). Das folgende Beispiel zeigt die Syntax:
    PP_LUATUT01_13_For-Generisch.png


    2 Ein paar Hinweise


    Dieser Artikel gab einen Einblick in Lua und hat Grundlagen der Sprache vorgestellt.
    Nun beherrscht man die Sprache Lua im Allgemeinen.
    Doch was würde man ohne die Standardbibliotheken machen? Es gibt vorgefertigte Funktionen,
    zum Beispiel eine Mathematikbibliothek.
    Zudem wurde noch nicht auf Besonderheiten bezüglich RailWorks eingegangen.
    Dies folgt in den Artikeln:
    Das Lua-Tutorial: #2 – Die Lua-Bibliotheken
    Das Lua-Tutorial: #3 – Besonderheiten und Funktionsübersucht

Teilen