Lua - Mythen, Märchen und die Mondfinsternis

Achte bitte darauf, dass Du mit deinem Thema bzw. mit deiner Frage im richtigen Bereich bist.
Die Bereiche sind: Einstellungen im Forum, EEP aktuell ab EEP7 , Splines, Rollmaterialien, Immobilien, Landschaftselemente, Signale und Schaltung, Anlagenvorstellungen, Schnappschüsse Konstrukteure, EEP Treffen , Laberecke, Online - Handbuch EEP Vielen Dank für die Unterstützung das Forum übersichtlich zu halten.
Bilder/Fotos aus dem Internet sind nur als Link gestattet. Eigene Fotos, also Fotos aus dem realen Leben, sind erstens mit Eigenes Bild als Quellenangabe zu kennzeichnen und zweitens nur als Dateianhang im Beitrag zulässig. Bilder ohne Quellenangaben und Bilder dessen Quelle das Internet wie z. B. Google ist, werden gelöscht.
  • Welche Funktion muss vor und welche muss hinter der EEPMain() stehen?

    Jede Funktionsdefinition muss vor ihrem Funktionsaufruf stehen.

    Und damit ist schon alles erklärt.

    Ich will es trotzdem ein wenig ausführlicher erklären. Weil da offenbar Mythen kursieren.

    Ob irgendeine Funktionsdefinition vor der Definition der Funktion EEPMain() steht oder dahinter, ist völlig egal!

    Denn die Funktion EEPMain() wird von EEP erst dann das erste Mal aufgerufen, wenn das komplette Skript abgearbeitet ist. Ganz egal, wo sie im Skript definiert wurde. Wenn die EEPMain() aufgerufen wird, sind also alle Funktionen im Skript definiert und stehen zur Verfügung. Dasselbe gilt für sämtliche Funktionen, die per Kontaktpunkt aufgerufen werden. Die dürfen ebenfalls an beliebiger Stelle im Skript stehen. Weil die ganze Anlage erst losläuft, nachdem das komplette Skript abgearbeitet wurde. Vorher kann kein Zug über einen KP fahren und eine Funktion auslösen.

    Macht euch das bitte immer wieder klar:

    Das Schlüsselwort function heißt so etwas wie merk dir diese Schritte

    Es leitet eine Funktionsdefinition ein. Der Computer wird nur aufgefordert, sich die folgenden Befehlsschritte unter einem Namen zu merken. Mehr nicht! An dieser Stelle wird nichts ausgeführt. Das Skript erstellt sozusagen ein Arbeitsbuch. Und dieses Buch wird nicht sortiert.

    Ein Funktionsaufruf bedeutet dann nichts anderes als "Mach jetzt das, was in deinem Buch unter diesem Funktionsnamen steht!". Und dafür ist es völlig egal, an welcher Stelle es im Buch steht. Deshalb haben Funktionen einen Namen. Damit der Computer anhand des Namens die Befehlsliste findet, die er ausführen soll.

    Lua
    function Beispiel()    -- merk dir, was du tun sollst wenn ich es dir sage
        print("Hallo EEP") -- nicht jetzt, sondern beim Aufruf der Funktion
    end
    
    Beispiel()             -- tu es jetzt!


    Wird der folgende Code funktionieren?

    Ja, selbstverständlich funktioniert der.

    In der EEPMain() steht zwar der Funktionsaufruf Beispiel(). Und damit steht er in der Zeile 2. Also im Skript über der Zeile 6, in der die Definition der Funktion Beispiel() beginnt.

    Aber der Aufruf findet erst dann statt, wenn die EEPMain() ausgeführt wird. Also nach der Definition der Funktion Beispiel(). Egal, ob die Definition der Funktion Beispiel() im Skript vor oder hinter der Definition der EEPMain() steht.

    Und in der Funktion Beispiel() wird eine Variable mit Namen Zahl hochgezählt, die erst weiter unten im Skript deklariert wird. Das funktioniert deshalb, weil das Hochzählen erst dann stattfindet, wenn die Funktion Beispiel() ausgeführt wird. Für die Ausführung muss die Variable zwingend deklariert sein. Aber eben erst dann. Und ausgeführt wird Beispiel() erst, nachdem das komplette Skript schon abgearbeitet wurde.

    Nebenbemerkung: Es ist kein guter Stil, eine Variable in eine Funktion einzusetzen bevor sie deklariert wurde. Und in anderen Programmiersprachen wird sofort gemeckert, wenn man das tut. Aber nur deshalb, weil das Risiko viel zu groß ist, dass man die Deklaration ganz vergisst.

    Schlusswort:

    Die Aussage, dass es Funktionsdefinitionen gäbe, die vor der EEPMain() stehen müssten und solche, die dahinter gehören, ist blühender Unsinn, der in mondfinsterer Nacht zusammengebraut wurde.

  • Besser kann man es nicht erklären,

    ergänzend möchte ich den Sonderstatus der Funktion EEPMain() hervorheben,

    die wird nämlich im EEP-Script überhaupt nicht aufgerufen, sondern sie ist

    leider die einzige EEP-Funktion die uns Trend zum Spielen zur Verfügung

    gestellt hat, als Deklaration einer Funktion.

    Gruß Dieter

    1.MSI 17,3" Intel® i7-8750H 16GB SSD + HDD GeForce® GTX 1060 »GV72 8RE-013DE (00179E-013)

    2. PC:Win10/64, i7-7700K, 4.2 GHz, GPU GTX 1070/8 GB, 16 GB RAM(DDR4), SSD 960 Evo 500GB, Ilyama PL2490
    EEP 6.1 - EEP 15 E, HomeNos 15

    Ich wünsche mir eine freizügige Script-Sprache und eine leistungsfähige Grafik Engine für EEP.
    Ein Leben ohne EEP ist möglich, aber sinnlos, so ganz sicher bin ich mir nicht mehr.

    "mal was ganz Einfaches" "rundum sorglos Paket"

    Parrys YouTube Videos
    https://www.twitch.tv/parry_36/

  • Sonderstatus der Funktion EEPMain()

    Die EEPMain() Funktion ist beileibe nicht die einzige Funktion, die von EEP aufgerufen wird.

    Dasselbe gilt auch für jede Funktion, die man aus einem Kontaktpunkt aufruft.

    Und für die Callback Funktionen:

    • EEPOnSignal_ID()
    • EEPOnSwitch_ID()
    • EEPOnTrainExitTrainyard() - neu!
    • EEPOnTrainCoupling() - neu!
    • EEPOnTrainLooseCoupling() - neu!

    Jede dieser Funktionen wird durch ein Ereignis in EEP aufgerufen. Im Skript werden all diese Funktionen lediglich definiert.

    Für die Funktion EEPMain() ist das auslösende Ereignis der Ablauf eines Timers mit 200 Millisekunden Dauer.

    Niemand käme auf die Idee, zwei Hauptschleifen in einem Programm haben zu wollen. Ich verstehe deshalb nicht, warum du "leider" schreibst. Gibt es da vielleicht noch Erklärungsbedarf, wie man eine Hauptschleife in einem Programm nutzt?

  • Ach Goetz,

    was soll das, ich glaube nicht, dass du das verwechselst.

    Die EEPMain() ist die einzige Funktion, die im Script declariert ist,

    alle anderen EEP -Funktionen werden uns von Trend als fertige,

    benutzbare Funktionen, die Trend in EEP deklariert hat, geliefert.

    Nur die EEPMain() können wir nach Belieben ausschmücken.

    Für mich gibt es keinen Erklärungsbedarf, allerdings wäre es nicht

    schlecht, wenn Trend uns an die Lua/EEP Schnittstelle ließe.

    Liebe Grüße dein Freund Dieter

    1.MSI 17,3" Intel® i7-8750H 16GB SSD + HDD GeForce® GTX 1060 »GV72 8RE-013DE (00179E-013)

    2. PC:Win10/64, i7-7700K, 4.2 GHz, GPU GTX 1070/8 GB, 16 GB RAM(DDR4), SSD 960 Evo 500GB, Ilyama PL2490
    EEP 6.1 - EEP 15 E, HomeNos 15

    Ich wünsche mir eine freizügige Script-Sprache und eine leistungsfähige Grafik Engine für EEP.
    Ein Leben ohne EEP ist möglich, aber sinnlos, so ganz sicher bin ich mir nicht mehr.

    "mal was ganz Einfaches" "rundum sorglos Paket"

    Parrys YouTube Videos
    https://www.twitch.tv/parry_36/

    Einmal editiert, zuletzt von Parry36 (18. Dezember 2018 um 17:01)

  • Nur die EEPMain() können wir nach Belieben ausschmücken.

    Nein, Dieter.

    Jede der von mir genannten Funktionen schmückt man ebenfalls selbst nach Belieben aus. Ganz genauso, wie die EEPMain() auch. Sie werden alle von EEP aufgerufen und der User bestimmt im Skript, was dann passieren soll.

    Im Gegensatz zu Funktionen wie EEPSetSignal(), welche in der Tat nur mit Parametern aufgerufen werden, aber fest definiert sind.

  • Nein, lieber @Goetz,

    so einfach ist das nicht, die EEPOn ..() Callback-Funktionen, werden nicht von EEP aufgerufen,

    sondern indirekt initiiert durch die Ereignisse die der User bestimmt.

    Aber ich möchte hier nicht weiter um des "Kaisers Bart" streiten,

    eigentlich ging es mir nur darum, festzustellen, dass die EEPMain() im Script an keiner

    Stelle aufgerufen wird, sondern nur deklariert ist.

    Habe fertig.

    1.MSI 17,3" Intel® i7-8750H 16GB SSD + HDD GeForce® GTX 1060 »GV72 8RE-013DE (00179E-013)

    2. PC:Win10/64, i7-7700K, 4.2 GHz, GPU GTX 1070/8 GB, 16 GB RAM(DDR4), SSD 960 Evo 500GB, Ilyama PL2490
    EEP 6.1 - EEP 15 E, HomeNos 15

    Ich wünsche mir eine freizügige Script-Sprache und eine leistungsfähige Grafik Engine für EEP.
    Ein Leben ohne EEP ist möglich, aber sinnlos, so ganz sicher bin ich mir nicht mehr.

    "mal was ganz Einfaches" "rundum sorglos Paket"

    Parrys YouTube Videos
    https://www.twitch.tv/parry_36/

  • die EEPOn ..() Callback-Funktionen, werden nicht von EEP aufgerufen

    Oh. :ar_1: Von wem dann? :as_1:

    fragt sich

    Christopher
    (aber nicht ganz ernsthaft)

    PC: Intel i7-7700K; 64bit; 4,2 GHz; 32GB RAM; GeForce GTX 1080 (8 GB); Win 10, 22H2; EEP 6, 15 (Dev), 17.2 Plugins 1+2 (Dev); HomeNOS 17 (Dev)
    Laptop: Intel i7-12700H; 64bit; 2,7 GHz; 16GB RAM; GeForce RTX3070Ti (8 GB); Win 11, 23H2; EEP 6, 17 Plugins 1+2; HomeNOS 17 (User)

  • cetz,

    warum fragst du, du weißt es doch.

    1.MSI 17,3" Intel® i7-8750H 16GB SSD + HDD GeForce® GTX 1060 »GV72 8RE-013DE (00179E-013)

    2. PC:Win10/64, i7-7700K, 4.2 GHz, GPU GTX 1070/8 GB, 16 GB RAM(DDR4), SSD 960 Evo 500GB, Ilyama PL2490
    EEP 6.1 - EEP 15 E, HomeNos 15

    Ich wünsche mir eine freizügige Script-Sprache und eine leistungsfähige Grafik Engine für EEP.
    Ein Leben ohne EEP ist möglich, aber sinnlos, so ganz sicher bin ich mir nicht mehr.

    "mal was ganz Einfaches" "rundum sorglos Paket"

    Parrys YouTube Videos
    https://www.twitch.tv/parry_36/

  • Um das noch einmal abschließend zu sagen:

    Der Sonderstatus der Funktion EEPMain()

    gehört zu den Mythen.

    Richtig ist, dass man die speziellen EEP-Funktionen, die man per Lua nutzen kann, in zwei Kategorien unterteilt muss:

    Funktionen, die in EEP definiert sind und aus dem Skript aufgerufen werden:

    Dazu gehört beispielsweise EEPSetSignal()

    Man schreibt einfach den Funktionsaufruf mit den gewünschten Argumenten ( = Signalnummer und -stellung) an geeigneter Stelle ins Skript. Der Befehl geht so an EEP und dort ist die Funktion (für uns unsichtbar) definiert.

    Funktionen, die man selbst im Skript definieren muss und die von EEP aufgerufen werden:

    Dazu gehört die EEPMain() Funktion.

    Ebenso gehören die Callback Funktionen dazu, welche alle mit EEPOn beginnen.

    Und vor allen Dingen gehören dazu alle selbst geschriebenen Funktionen, die man per Kontaktpunkt aufruft.

    Für solche Funktionen schreibt man ins Skript nur eine Funktionsdefinition. Eine Befehlsliste welche dann ausgeführt wird, wenn EEP diese Funktion aufruft.

    Der Funktionsaufruf steht in diesen Fällen nicht im Skript. Der Aufruf erfolgt durch EEP. Ausgelöst durch ein bestimmtes Ereignis.

    Für die EEPMain() Funktion ist der Auslöser der Ablauf einer Eieruhr. Wenn die auf 0 ist, dann ruft EEP die Funktion EEPMain() auf. Und wenn diese Funktion eine andere Zahl als 0 zurückgibt, dann wird die Eieruhr erneut auf 200 Millisekunden gesetzt und gestartet.

    Für EEPOnSignal_ID() ist das auslösende Ereignis das Umschalten eines Signals. Für EEPOnTrainCoupling() ist der Auslöser das Ankuppeln.

    All solchen Funktionen gemein ist die Tatsache, dass EEP die Ausführung anstößt und nicht das Skript. Und weil man die Bedingungen kennt, die zum Aufruf führen, weiß man beim Aufruf etwas über den aktuellen Stand. Beim Aufruf der EEPMain() weiß man, dass 200 Millisekunden in EEP abgelaufen sind. Beim Aufruf von EEPOnSignal_42() weiß man, dass das Signal 42 umgeschaltet hat. Beim Aufruf der Funktion EEPOnTrainCoupling() weiß man, dass etwas angekuppelt wurde (und in den Argumenten steht, welche beiden Einheiten gekuppelt wurden) und so weiter.

    Schlusswort:

    In der Lua Anleitung steht bei jeder Funktion, ob sie zur ersten oder zur zweiten Kategorie gehört. Genauer: Bei jeder Funktion steht, wo sie definiert und wo sie aufgerufen wird.

  • Hallo Götz,

    das ist eine sehr anschauliche und verständliche Erklärung.

    Vielleicht noch ein Hinweis: Ich kann Funktionen im Script definieren die nur

    im Skript selbst aufgerufen werden um z.B. gleich Abläufe die an verschiedenen

    Stellen vorkommen durch nur einen Aufruf der Funktion zu vereinfachen.

    Gruß

    Frank

    Windows 10 Pro Version 2004 / Intel i9-9900K 8x3,6 GHz / 64 GB RAM NVIDIA GeForce RTX 2080 SUPER / 8 GB GDDR6 DirectX 12.0

    Acer Windows 10 Home64 I7-6700HQ 2,6 GHz / 16 GB RAM Geforce GTX 960M 4096 MB GDDR5

    EEP bis 16.4 Plug-In 1-4

  • Tabellen sind für Anfänger zu schwer

    Das ist mein Lieblingsmythos. Weil er häufig zum Einsatz von Methoden führt, die viel umständlicher und undurchsichtiger als eine Tabelle sind.

    Eine Tabelle fasst mehrere Werte unter einem Namen zusammen. Alleine das macht sie schon sehr praktisch.

    Wie bei einer Variablen wählt man einen Namen und schreibt dann mit einem = Zeichen dahinter, was unter diesem Namen gespeichert werden soll. Aber statt eines einzelnen Wertes kann man mehrere hintereinander schreiben. Alle zusammen müssen in geschweiften Klammern stehen. Und die einzelnen Werte werden durch ein Komma voneinander getrennt:

    Text = {"Hallo", "EEP", "Ich", "lerne", "Lua"}

    Die einzelnen Elemente der Tabelle werden bei dieser Zuweisung automatisch durchnummeriert. Das Wort Hallo im obigen Beispiel ist also der erste Wert, das Wort EEP ist der zweite und so weiter.

    Über diese Nummern kann ich mir bei Bedarf die Werte holen:

    Text[1] ist die Schreibweise für das erste Element in der Tabelle Text

    Ohne eine Tabelle hätte jedes Wort einen eigenen Namen bekommen:

    Wort_1 = "Hallo"

    Wort_2 = "EEP"

    Wort_3 = "Ich"

    etc.

    Wenn sie alle in einer Tabelle stecken, dann kann ich sie über die Tabellenplätze auswählen

    Text[1] , Text[2] , Text[3]

    Die Nummer der gewünschten Zelle schreibt man in eckige Klammern.

    Der Vorteil:

    Die Nummer in den Klammern kann ich durch eine Variable ersetzen:

    Lua
    for i = 1, 3 do
        print(Text[i])
    end

    schreibt die ersten drei Worte untereinander.

    Und Lua weiß, wieviele Elemente in einer Tabelle stehen. Diese Anzahl bekommt man mit einem # vor dem Tabellennamen. Mit #Text bekomme ich also in meinem Beispiel den Wert 5:

    Lua
    for i = 1, #Text do
        print(Text[i])
    end

    schreibt alle fünf Worte aus der Tabelle untereinander.

    Und wenn man eine Tabelle so abarbeitet, dann hat man gleichzeitig auch zu jedem Zelleninhalt die zugehörige Nummer parat.

    Das macht die Sache noch praktischer.

    Wenn man zum Beispiel einen Zugzielanzeiger mit Texten beschicken will, dann kann man diese Texte einfach in der richtigen Reihenfolge in eine Tabelle schreiben:

    Anzeige = {"ICE 517","Nürnberg","Kassel","17:48"}

    Die Textfelder auf der Tafel haben einfach nur Nummern. Wie bekommt man jetzt also die passenden Texte in die verschiedenen Felder?

    Lua
    for i = 1, #Anzeige do
        EEPSetTextureText("#321", i, Anzeige[i])
    end

    schreibt die Texte aus der Tabelle in die Felder.

    Und wenn die Liste 8 Texte enthalten würde, für 8 Zeilen auf der Anzeige, dann wäre der Code genau derselbe. Weil der Code die selbe Routine so oft wiederholt, bis alle Texte aus der Tabelle übertragen wurden.

    Ohne den Einsatz einer Tabelle stünde im Skript so etwas:

    Lua
    EEPSetTextureText("#321", 1, "ICE 517")
    EEPSetTextureText("#321", 2, "Nürnberg")
    EEPSetTextureText("#321", 3, "Kassel")
    EEPSetTextureText("#321", 4, "17:48")

    und vielleicht noch vier weitere Zeilen dieser Art.

    Oder - noch viel schlimmer - so etwas:

    Was genau steht da also in diesen drei Zeilen, die das ganze so schön überschaubar machen?

    Lua
    for i = 1, #Anzeige do
        EEPSetTextureText("#321", i, Anzeige[i])
    end

    In Zeile 1 steht, dass er der Variablen namens i zuerst den Wert 1 und dann immer höhere Zahlen geben soll, bis sie genauso groß ist, wie die Anzahl Elemente in der Tabelle Anzeige. Und mit jeder dieser Zeilen wir dann das durchgeführt, was in der folgenden Zeile steht.

    Beim ersten Durchlauf heißt der Befehl in Zeile 2 also:

    EEPSetTexture("#321", 1, Anzeige[1]

    weil für i die Zahl 1 eingesetzt wird. Und in der Tabelle Anzeige steht an Position [1] der Text "ICE 517". So kann man ganz einfach den richtigen Bezug zwischen Tabelle und Textfeldern herstellen.

    Und wenn eine Anzeigetafel für jedes Textelement zwei Felder hat? Eins auf der Vorder- und eins auf der Rückseite? Muss man dann in der Tabelle alle Texte doppelt eintragen?

    Nein. Das wäre zwar eine Möglichkeit, aber es ist nicht schlau.

    Man kann sich stattdessen überlegen, wie die Textfelder und die Tabellenplätze zusammenhängen:

    1 -> 1

    2 -> 1

    3 -> 2

    4 -> 2

    5 -> 3

    6 -> 3

    7 -> 4

    8 -> 4

    Weil jedes Feld doppelt vorkommt, steigen die Feldnummern auch doppelt so schnell an.

    Also:

    Zum Textfeld Nummer 2 passt der Text 1

    Zum Textfeld Nummer 4 passt der Text 2

    etc.

    Und die anderen Textfelder? Die mit den ungeraden Nummern?

    Deren Nummer ist immer genau 1 kleiner als das Doppelte der Tabellennummer.

    Alles klar?

    Lua
    for i = 1, #Anzeige do
        EEPSetTextureText("#321", i * 2    , Anzeige[i])
        EEPSetTextureText("#321", i * 2 - 1, Anzeige[i])
    end

    So nutzt man für die Vorder- und die Rückseite den selben Text aus der Tabelle (wenn die Textfelder so wie bei den DB2 Zielanzeigern nummeriert sind.)

    Der Vorteil einer Tabelle liegt vor allem darin, dass man die Elemente alle an einer Stelle und unter einem Namen zusammen hat. Das macht Änderungen einfacher, weil man sie zentral an einer Stelle vornehmen kann. Ein weiterert Vorteil ist die sortierte Sammlung.

    Und was mit den Texten so schön klappt, das klappt auch für die Lua-IDs der Anzeiger auf einem Bahnsteig:

    Lua
    Anzeige = {"ICE 517","Nürnberg","Kassel","17:48"}
    Bahnsteig_1 = {"#312", "#313", "#324", "#341"}
    
    for Immobilie = 1, #Bahnsteig_1 do
        for i = 1, #Anzeige do
            EEPSetTextureText(Immobilie, i * 2    , Anzeige[i])
            EEPSetTextureText(Immobilie, i * 2 - 1, Anzeige[i])
        end
    end

    beschriftet die Textfelder von vier ZZAs mit dem selben Text.

    Der nächste Schritt wäre nun, dass man die Anzeigetexte den verschiedenen Zügen zuordnet. Aber das hebe ich mir für ein weiteres Posting auf.

    Schlusswort:

    Wer Zeilen zig-fach kopiert und immer ein wenig abändert, der programmiert nicht. Der versucht noch nicht einmal, zu programmieren. Programmieren heißt, dass man sich überlegt wie man den Computer veranlasst diese Wiederholungen zu machen.

    Programmieren heißt: Muster erstellen.

    Einmal editiert, zuletzt von Goetz (20. Dezember 2018 um 09:43)

  • Hallo,

    Wer Zeilen zig-fach kopiert und immer ein wenig abändert, der programmiert nicht. Der versucht noch nicht einmal, zu programmieren. Programmieren heißt, dass man sich überlegt wie man den Computer veranlasst diese Wiederholungen zu machen.

    Programmieren heißt: Muster erstellen.

    wenn mir ein Fachinformatiker Anwendungsentwicklung in der mündlichen Abschlussprüfung die Frage was ist Programmierung so beantwortet, ist er so gut wie durchgefallen. Abgesehen ist das Schlusswort sehr abfällig denen gegenüber, die froh sind wenn sie in EEP mit LUA überhaupt etwas so hinbekommen, dass es genau das macht was sie sich gedacht haben. Es sind keine Profis.

    Gruß Holger (HG3)

    Desktop: EEP 6 - 16 (installiert 14 bis 17) aktuell 17.2 + Plugin 1 u. 2, HN14 und 16, Intel i7 3770 3,5 GHz, 24 GB RAM, NVIDIA GeForce GTX 1060 6GB Grafikspeicher, Windows 10

  • wenn mir ein Fachinformatiker Anwendungsentwicklung in der mündlichen Abschlussprüfung die Frage was ist Programmierung so beantwortet, ist er so gut wie durchgefallen

    Jetzt wird's aber arg philosophisch. Auch mit blindem Nachplappern von auswendig Gelerntem kann man in einer Prüfung erfolgreich sein. @Goetz ens Anliegen ist halt, dass man auch versteht - oder "durchdringt", wie es ein Lehrer an meiner Schule mal genannt hat - was man da macht. Beim "Malen nach Zahlen" habe ich am Ende auch ein Bild an der Wand hängen. Malen kann ich dann aber trotzdem noch nicht, wenn ich nicht versuche, mich von der Vorlage zu lösen.

  • Hallo,

    grundsätzlich ist das schon richtig was Götz schreibt.

    Man kann schon die rafiniertesten Routinen schreiben. Das Problem dabei ist, dass man dann nach ein paar Jahren wieder weis was man hier gemacht hat. Speziell dann wenn diese Routine ohne Kommentar nur so herumsteht. :aa_1:

    Gruß

    Alfred

    Intel Core I5 3550; RAM 24 GB; Windows 10 (64 Bit),
    Nvidia Geforce GTX 960 (4 GB)
    EEP Modelconverter, Modell-Explorer, Texturenmultiplier, Task-Memorizer, Status-Memorizer, Modell-Katalog
    EEP 6.1, EEP 16 Update 4, EEP 17 Update 2 Patch 1; Plugins: 1

  • Tabellen sind zu schwer - Teil 2

    Wenn man Werte für eine Tabelle einfach aneinanderreiht, dann werden sie automatisch durchnummeriert:

    Anzeige = {"ICE 517","Nürnberg","Kassel","17:48"}

    print(Anzeige[1]) gibt dann den Text "ICE 517" aus.

    Aber man kann auch jeden Wert unter einem eigenen Bezeichner ablegen:
    Anzeige = {["Zug"] = "ICE 517", ["Ziel"] = "Nürnberg", ["Ueber"] = "Kassel", ["Zeit"] = "17:48"}

    Bitte beachten: Die Bezeichner der Tabellenplätze sind Strings1. Also stehen sie in Anführungszeichen, wie alle Strings. Und sie müssen außerdem in eckige Klammern, wie alles, was einen Tabellenplatz kennzeichnet. Die eckige Klammer macht aus dem String einen Bezeichner. Die Zuordnung der Werte geschieht dann wie üblich über ein = Zeichen.

    Und so, wie die Bezeichner beim Anlegen der Tabelle benutzt werden, so werden sie auch beim Abruf benutzt. In eckigen Klammern und Anführungszeichen:

    print(Anzeige["Zug"]) gibt "ICE 517" aus.

    print(Anzeige[1]) gibt jetzt nil aus, weil die Zelle 1 in der Tabelle leer ist.

    Zellen mit Bezeichnern werden nicht durchnummeriert!


    Es gibt sowohl beim Anlegen der Tabelle als auch für den Abruf der Daten vereinfachte Schreibweisen. Beim Anlegen kann man die Klammern und Anführungszeichen um die Bezeichner weglassen:

    Anzeige = {Zug = "ICE 517", Ziel = "Nürnberg", Ueber = "Kassel", Zeit = "17:48"}

    Das ist bequemer und liest sich auch besser.

    Das = Zeichen hinter dem Bezeichner zeigt Lua, dass links davon ein Bezeichner steht und rechts davon der Wert, der unter diesem Bezeichner gespeichert werden soll.

    Beim Abruf kann man die Klammern und Anführungszeichen ebenfalls weglassen, muss dann aber Tabellennamen und Bezeichner mit einem Punkt trennen:

    print(Anzeige.Zug)

    Trotzdem ist es wichtig die Schreibweise mit den Klammern zu kennen. Man muss verstehen, dass es sich bei den Bezeichnern um Strings1 handelt. Denn in den eckigen Klammern kann man auch Variablen verwenden. Die dürfen den String enthalten, den man als Bezeichner verwenden möchte. Und in Anführungszeichen kann man Bezeichner bauen, die ohne Anführungszeichen nicht möglich wären. Weil sie in Anführungszeichen auch Sonderzeichen enthalten dürfen. Das # zum Beispiel. Oder Leerzeichen.

    Das bedeutet, dass man in einer Tabelle verschiedene Werte für verschiedene Züge festhalten kann:

    Lua
    Geschwindigkeit = {["#ICE 517"] = 160, ["#RE 12"] = 120, ["#Güter"] = 80}
    function freie_Strecke(dieserZug)
        EEPSetTrainSpeed(dieserZug, Geschwindigkeit[dieserZug])
    end

    Trägt man jetzt in einen Kontaktpunkt den Funktionsaufruf freie_Strecke ein, dann wird beim Überfahren des Kontaktpunktes diese Funktion aufgerufen und der Zugname als Argument übergeben. Lua benutzt diesen Zugnamen als erstes Argument für EEPSetTrainSpeed() um genau diesem Zug eine Geschwindigkeit zuzuweisen. Und es benutzt den Zugnamen auch, um aus der Tabelle Geschwindigkeit die Zahl rauszusuchen, die zu diesem Zug gehört. Diese Zahl wird dann als zweites Argument in EEPSetTrainSpeed() benutzt.


    Wenn der Zug, welcher den Kontaktpunkt überfährt, der RegionalExpress 12 ist, dann bekommt er die Geschwindigkeit 120:

    Wichtig! In Kontaktpunkten müssen die Funktionsnamen ohne Klammern2 und ohne Argument stehen. Also im Beispiel nur freie_Strecke. EEP gibt seit Einführung des Plug-In 2 zu EEP 11 beim Funktionsaufruf immer den Zugnamen als Argument mit. (Siehe Lua Handbuch Seite 48)

    Mit solchen Zuordnungstabellen kann man sich lange Ketten von if-Vergleichen sparen. Lua findet so mit einem Griff den passenden Wert, statt der Reihe nach viele Vergleiche anzustellen bis es den Namen gefunden hat, der passt. Was dann so aussehen würde:

    Lua
    function freie_Strecke(dieserZug)
        if dieserZug == "#ICE 517" then
            EEPSetTrainSpeed("#ICE 517", 160)
        elseif dieserZug == "#RE 12" then
            EEPSetTrainSpeed("#RE 12", 120)
        elseif dieserZug == "#Güter" then
            EEPSetTrainSpeed("#Güter", 80)
        end
    end

    Bei dieser Methode muss Lua drei Vergleich anstellen, bis es den Güterzug gefunden hat. Bei mehr Zügen entsprechend mehr Vergleiche.


    Fußnoten

    1Strings:

    Als Strings bezeichnet man in der Programmierung alles, was als Text behandelt werden soll. Also Worte, die keine Namen von Variablen, Tabellen oder Funktionen sind, sondern eben Worte.

    Hallo ist der Name einer Variablen

    "Hallo" ist ein String.

    print(Hallo) gibt das aus, was in der Variable Hallo gespeichert wurde.
    Wenn es diese Variable nicht gibt, dann ist das nil.

    print("Hallo") gibt das Wort Hallo aus.

    2Funktionsnamen ohne Klammern:

    Weitere Argumente in Klammern erfordern die Verwendung von Bennys Codezeile.

    Einmal editiert, zuletzt von Goetz (20. Dezember 2018 um 07:32)

  • Man kann schon die rafiniertesten Routinen schreiben. Das Problem dabei ist, dass man dann nach ein paar Jahren wieder weis was man hier gemacht hat. Speziell dann wenn diese Routine ohne Kommentar nur so herumsteht.

    Hallo Alfred,

    dem kann ich nur zustimmen. Ich kann jedem nur raten zu jeder routine Erklärungestexte dazuzuschreiben (was wird gemacht, warum, wie, etc). dann kann man das auch nach langer Zeit wieder "erfassen" und verstehen. Meine Programme enthalten oft bis zu 50% Bemerkungen, das hilft!

    Um eventuelle Fragen zu vermeiden:
    Bemerkungen werden in Lua mit "--" markiert, also das sieht dann z.B. so aus:

    function Textanzeige()

    -- Das ist meine Routine zum Anzeigen von einem Text

    print("Das ist mein Text")

    end

    Gruß Volker

  • Hallo Goetz,

    ich hoffe doch, dass deine Dokumentation über die Verwendung von Tabellen in Bezug auf LUA Funktionen im EEP auch im LUA Handbuch bzw. als Tutorial erscheinen.

    Ich habe sie zumindest einmal in meine LUA-Doku.-Sammlung aufgenommen.

    Gruß

    Alfred

    Intel Core I5 3550; RAM 24 GB; Windows 10 (64 Bit),
    Nvidia Geforce GTX 960 (4 GB)
    EEP Modelconverter, Modell-Explorer, Texturenmultiplier, Task-Memorizer, Status-Memorizer, Modell-Katalog
    EEP 6.1, EEP 16 Update 4, EEP 17 Update 2 Patch 1; Plugins: 1

  • im LUA Handbuch bzw. als Tutorial

    Das Lua-Handbuch ist nicht mehr in meiner Hand. Das muss jetzt derjenige weiterführen, der meinen alten Posten in der Firma füllt. Aber das Lua-Handbuch zu EEP enthält sowieso keine Grundlagen zu dieser Programmiersprache. Weil es dazu schon genügend umfangreiche Werke gibt.

    Das Handbuch konzentriert sich auf die Besonderheiten der Funktionen, welche die EEP-Entwickler als Bindeglied zwischen EEP und Lua geschaffen haben. Und die Tabellen sind keine Besonderheit für EEP.

    Video Tutorials - genauer gesagt Twitch Sendungen - zum Thema Tabellen gibt es schon von mir.