Christophers Lua-Module

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.
  • Hallo zusammen,

    im Zuge des Baus mehrerer Anlagen hat sich bei mir ein rundes Dutzend Lua-Module angesammelt, von denen jedes eine spezifische Aufgabe erledigt und unabhängig von der konkreten Anlage programmiert ist. Diese Module sind also relativ generisch geschrieben. Und weil ich vermute, dass hier ein gewisses Interesse besteht, habe ich mir vorgenommen, sie nach und nach im Sinne eines Tutorials vorzustellen. Bitte erwartet nicht, dass sie in allen Fällen genau Euer Bedürfnis erfüllen. Ich möchte auch keine Gewähr dafür übernehmen, dass sie fehlerfrei sind. Vielmehr sollen Euch meine Überlegungen hinter den einzelnen Modulen ermutigen, Euch selbst einen Baukasten von Modulen aufzubauen, die für Eure Bedürfnisse passen.

    Mein primäres Nachschlagwerk für Lua ist übrigens https://lua.coders-online.net/, weil es so schön kompakt und schnörkellos ist (ich weiß: nicht jedermanns Sache).

    Wenn Ihr ein bisschen Interesse signalisiert (Likes oder Antworten), wird es zügig mit einem "Kapitel 1. Überlegungen zum Programmierstil" losgehen, bevor ich das erste wirklich Modul präsentiere.

    Bis bald

    Christopher

    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)

    2 Mal editiert, zuletzt von cetz (12. Dezember 2018 um 13:32) aus folgendem Grund: LUA -> Lua

  • Puhh lieber cetz.

    Jetzt sollte ich doch mal mich mit LUA näher befassen, habe bisher alles mit Kontaktpunkten geregelt.


    Eine lieben Gruß, j.krae

    Int.Core i7-4790 3,6GHz,16GB,GTX960 4GB,Win10-64Pro HD: 200GB+1TB für Sohn EEP 6,10,13,14,16

    Lapt.HP17 i7950H 2,6GHz,16G-Ram, GTX1650, Win10 Home 64, 256G-SSD,1TB-HD

    Int.Core i5-3570 3,8GHz,32GB-Ram, GTX1650,W.10Pro64 >SSD 120G, 2x2TB-HD, LCD PH27Cu+Fuji24, EEP6,10-17

    Train-Sim,Mod.-Expl.,-Kat.,-konv.,Zug-Expl.,MET,Bilderscan.

    HNO13,Blender 2,79,Makehum. <lernen

    Motto: Es gibt keine Probleme, nur Aufgaben

    Sonst: Alllroundhandwerker, PC-Doktor, EEP-Fan

  • Ich bin immer an Vorlagen für Scripte interessiert, die man dann auch für sich selbst abändern kann.

    Gruss

    Peter

    Betriebsystemname: Microsoft Windows 11 Pro

    Prozessor:Prozessor AMD Ryzen 7 7700X 8-Core Processor, 4501 MHz, 8 Kern(e), 16 logische(r) Prozessor(en)

    PC:RAM 32 GB, Name NVIDIA GeForce RTX 4070 Ti

    EEP 17 mit allen Plug ins und Patches

    Modelkonverter, Home-Nostruktor 16.0, Modellkatalog, Bodentextur Tool

  • Hallo Christopher,

    das Vorhaben finde ich Klasse, beide Augen und Ohren werde ich der Sache widmen,

  • Hallo Christopher, dein Modul (vielleicht muss es eine andere Bezeichnung erhalten) wird der EEP-LUA-Programmierung bestimmt einen Schub geben. Bin gespannt.

    Viele Grüße

    Volker

    EEP16 Expert, 64 Bit Win10 Home , 10 GB RAM i5-3350P 3.10 GHz Quad, NVIDIA Geforce GTX 730
    DirectX Version 12, StatusMemorizer, Hugo, keine weitere Zusatztools.

  • Hallo Christopher,

    dein Vorhaben scheint sehr interessant und eröffnet mir die Möglichkeit, endlich von den Kontaktpunkten wegzukommen.

    Behalte Dich im Auge :ae_1:

    Viele Grüße aus Berlin :aq_1:

    Manfred

    "Es kommt nicht darauf an, mit dem Kopf durch die Wand zu gehen, sondern mit den Augen die Tür zu finden." :ae_1: (Werner von Siemens)

    EEP X Edition: Die Schiefe Ebene, EEP 13.2.2 Expert, EEP 14.1.1 Expert, EEP 15.1 Expert, EEP 16.1.2 Expert, EEP 17.2.1.1

    Kiebel Titan Gaming-PC WIN 11 Pro (27", AMD Ryzen 7 7700X 8 Core 4,50 GHz, Radeon RX 7700 XT 12 GB, 32 GB RAM, 3000 GB HDD, 2000 GB SSD, WLAN )

  • Nun gut. Dann also los mit

    Kapitel 1. Überlegungen zum Programmierstil

    1.1 Namensraum

    Für etwas gefährlich halte ich, dass man überall in Lua globale Variablen und Funktionen definieren kann. Natürlich überschreibt eine spätere Variable oder Funktion die zuvor definierte. Aus diesem Grund versuche ich immer, den Namensraum so wenig wie möglich zu "verschmutzen".

    In meinen Modulen gibt es meist nur eine einzige globale Variable und die heißt so wie das Modul. Sie wird in aller Regel auch am Ende des Moduls zurückgegeben, so dass man vielleicht sogar hier auf die globale Definition verzichten könnte. Trotzdem finde ich es übersichtlicher, dass es eine Variable mit dem Namen des Moduls gibt.

    Weil ein Modul immer einiges an Funktionalität zur Verfügung stellt, ist die Variable keine Zahl und keine Zeichenkette, sondern eine Tabelle. Diese enthält dann die Funktionen (meistens sind es nur Funktionen), die das Modul sozusagen "exportiert".

    1.2 Einheitlichkeit

    Es hat sich als günstig erwiesen, dass jedes Modul im Grunde gleich aufgebaut ist. So finde ich mich auch nach längeren Unterbrechungen noch darin zurecht.

    Jedes Modul hat folgende Struktur:

    • Definition von lokalen Variablen, die ich im ganzen Modul brauche
      Sie sind sozusagen Modul-global, aber nicht welt-global.
    • Definition von lokalen Funktionen
      Diese brauche ich nur innerhalb des Moduls
    • Definition der zu exportierenden Funktionen
      Diese exportiere ich dann in der Variablen, die den Namen des Moduls hat.
    • Definition eben dieser Variablen als Tabelle mit den zu exportierenden Bestandteilen
      Ein Feld heißt immer _VERSION und enthält die Version des Moduls.
    • Eine print()-Anweisung, die den Modulnamen und die Version ausgibt
      So sieht man bei jedem "Skript neu laden", welche Module insgesamt gezogen werden.
    • Rückgabe der "Modul-Variablen" mittels return.

    1.3 Versionsverwaltung

    Natürlich sind meine Module nicht für die Ewigkeit in Stein gemeißelt. Vielmehr ändere ich sie von Zeit zu Zeit, mal mehr und mal wenig massiv. Damit ich dabei nicht durcheinander komme, verwende ich folgendes Schema:

    • Die erste Version ist immer 1.0
    • Bei reinen Erweiterungen, die bisher nicht enthalten waren, bleibt die Versionsnummer unverändert.
    • Wenn ich jedoch eine Funktion (oder etwas anderes) so ändere, dass die bisherige Verwendung nicht mehr garantiert werden kann, erhöhe ich die Versionsnummer.
      Das ist dann eine nicht abwärts-kompatible Änderung.

    Benutzt werden die Module durch require-Anweisungen. Dabei enthält der Aufruf die Versionsnummer. Damit ist sichergestellt, dass ich das Modul erreiche, das ich zum Zeitpunkt des Schrebens verwendet habe, und nicht etwa eine mittlerweile inkompatible Version.

    1.4 Sprache

    Es geht mir – vielleicht überraschenderweise – leichter von der Hand, in Englisch zu programmieren. Daher haben die Module englische Namen, ebenso wie die Variablen und Funktionen. Selbst bei Textausgaben, die dann im EEP-Ereignis-Fenster landen, bleibe ich meistens noch bei Englisch. Insofern werde ich Euch ein bisschen mit Englisch traktieren.

    Nur bei Kommentaren wechsele ich dann häufig ins Deutsche. Wobei ich eh wenig Kommentare schreibe. (Sie veralten einfach zu schnell und stimmen dann nicht mehr.) Die einzige Stelle, an der ich mich wirklich zu Kommentieren zwinge, ist die Beschreibung des Aufrufs der exportierten Funktionen. So weiß ich, was ich an Parametern übergeben muss, und welche Möglichkeiten ich hier habe.

    1.5 Layout

    Im Layout (Einrückungen, ein- oder mehrzeilige Anweisungen etc.) bin ich sehr stringent. Einrückungen werden sofort aufgeräumt, wenn zum Beispiel ein if dazwischen gerutscht ist. Ansonsten würde ich sehr schnell den Überblick verlieren.

    Das Ende von Anweisungen markiere ich immer mit einem Semikolon, auch wenn direkt ein Zeilenende folgt, und es eigentlich nicht notwendig wäre. So sehe ich aber mit einem Blick, ob die Anweisung am Zeilenende auch zu Ende ist, oder ob sie in der nächsten Zeile weiter geht. Fortsetzungszeilen bekommen übrigens noch eine Extra-Einrückung.

    Auch längere Kommentare schreibe ich, indem ich jede Zeile mit -- beginnen lasse. Block-Kommentare mit --[[, --[=[, --[==[, etc. verwende ich generell nicht, weil ich es zu schwer zu erkennen finde, ob ich nun gerade innerhalb eines Kommentars bin, oder außerhalb.

    1.6 Groß- und Kleinschreibung

    Bei Groß- und Kleinschreibung von Variablen- und Funktionsnamen habe ich eine ganz einfache Konvention:

    • Konstanten sind durchgängig groß geschrieben.
      Konstanten verwende ich, wenn ich sie mehrfach brauche und/oder an prominenter Stelle im Modul zur Anpassung bereit halte.
      Beispiel: local SENSE_OF_LIFE = 42; Dann kann ich im Modul immer wieder SENSE_OF_LIFE verwenden, und muss nicht 42 schreiben. Denn nach ein paar Stunden weiß ich nicht mehr, ob diese 42 jetzt eine Signal-ID, eine Gleis-ID, eine Minutenzahl oder was auch immer war.
    • Globale Variablen beginnen mit einem Großbuchstaben
      Zum Beispiel ist Debug bei mir eine globale Variable (und auch ein Modul).
    • Alles andere beginnt mit einem Kleinbuchstaben
      Zusammengesetze Variablennamen haben innen drin Großbuchstaben für eine leichtere Lesbarkeit, z.B. trainLength. Wikipedia hat mir gerade verraten, dass man das "Binnenmajuskeln" nennt; ich kannte nur den englischen Ausdruck "camelCase", den ich so schön anschaulich finde.
      Ausnahme dieser Regel: Einen Identifizierer nenne ich ID, z.B. signalID.

    Ich setze übrigens keine Typmarkierung an die Variablennamen. Ein abgefangener Rückgabewert heißt bei mir result oder success, aber nicht hResult, und eine Funktion könnte heißen doIt, aber nicht fDoIt.

    1.7 Fehler und Warnungen

    In den Funktionen gibt es immer wieder Fälle, an denen ich auf Situationen reagieren muss, die eigentlich nicht passieren sollen. Ich verwende auch lieber zu viele Sicherheitsabfragen als zu wenige, beispielsweise ob die Parameter einer Funktion wirklich übergeben wurden, oder ob sie von dem Datentyp sind, den ich erwarte.

    Hier unterscheide ich zwischen Fehlern und Warnungen:

    • Bei gravierenden Fehlern rufe ich die Lua-Funktion error() aus.
      Dadurch wird die ganze aktuelle Verarbeitung abgebrochen.
    • Bei Konstellationen, die nicht so schlimm sind, gebe ich einen Text mit print() aus, versuche aber die Funktion weiter laufen zu lassen.

    Generell habe ich bei meinen Anlagen immer das EEP-Ereignis-Fenster offen, um solche Informationen zu sehen. Den dummen Zähler aus der Standard-EEPMain() entferne ich natürlich, um mich auf das wesentliche zu konzentrieren.

    1.8 Lua für die konkrete Anlage

    Das Lua-Skript für eine konkrete Anlage ist bei mir immer ganz kurz. Es besteht nur aus ein paar Einstellungen für den Package-Pfad, für Debugging-Ausgaben und einem require()-Aufruf für das eigentliche Anlagen-Skript. Höchstens enthält es einmal kurzfristig ein paar weitere Zeilen zum Aufräumen der gespeicherten Werte oder für grundlegende Tests; diese fliegen aber so schnell wie möglich wieder raus.

    Erst das zweite Anlagen-Skript zieht dann die Module an, die die Anlage braucht. Und dieses Skript enthält alles Anlagen-Spezifische, also die Aufrufe in die Module hinein, um die Anlage soweit zu automatisieren, wie ich das eben will.


    So, jetzt würdet Ihr Euch schon mal in meinem Code etwas auskennen. Nur habt Ihr noch keinen gesehen. Das kommt dann im nächsten Kapitel.

    Bis denn

    Christopher

    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)

  • Christopher,

    planst du, deine Ausführungen später in einem Dokument zusammenzufassen oder ist es sinnvoll, sich direkt selber ein pdf aus deinen Textteilen zu erstellen?

    Der planvolle Aufbau beeindruckt mich auf jeden Fall schon mal.

    Danke für die hier investierte Arbeit.

    Viele Grüße :aq_1:

    Michael

    Kon seit Januar 2018, EEP 17.3 HomeNos 17

    DESKTOP-AQF4SPJ, AMD Ryzen 5 7600X 6-Core Processor 4.70 GHz, RAM 32,0 GB

    NVIDIA GeForce RTX 4060 8GB GDDR6

    Einmal editiert, zuletzt von Gärtner (MH3) (10. Dezember 2018 um 19:49)

  • Hallo Christopher,

    ich finde es toll, dass Du Dir die Zeit nimmst, um wieder ein paar (ich hoffe, recht viele) Einsteiger an Lua heranzuführen und dabei mit den Grundlagen der Programmierung anfängst. Das sind jetzt wahrscheinlich nicht die ganz heißen Sachen, auf die man als Einsteiger heiß ist, aber es sind sehr wichtige Dinge, die einem das (Programmier-)Leben ganz wesentlich erleichtern.

    Die meisten dieser Richtlinien m u s s man nicht so machen, um einen funktionsfähigen Code zu schreiben. Aber wenn man keinen ernsthaften Grund hat, es anders zu machen (z.B. eigene, bewährte und verfestigte Richtlinien), dann sollte man versuchen, sich möglichst genau an diese (oder andere, aber Richtlinien!) zu halten. Ordnung ist hierbei wirklich das halbe Leben und man ist (sich) sehr schnell dankbar dafür!

    Namensraum (als "Geltungsbereich" od. "Sichtweite" für den Anfang vielleicht verständlicher), Einheitlichkeit inkl. Groß-/Kleinschr. und Layout scheinen mir die wichtigsten Punkte hier, nicht nur weil sie weitgehend sprach-unabhängig sind.

    Ich kann alle nur ermutigen, sich zu trauen. Noch dazu, wenn sich wiederholt jemand solche Mühe macht. Es ist kein Hexenwerk und sieht komplizierter aus, als es ist. Die Denke dahinter ist nahezu die Gleiche, die in Euren Schaltkreisen steckt.

    Ich bin gespannt, wie es weiter geht! Und das, obwohl mein Einstieg schon Jahre zurückliegt.

    Viele Grüße
    Thomas

    - - - - -
    MidiTower (i7 9700KF 3,6×3,6GHz, 16GB RAM, 4GB GeForce RTX2070 super, WIN 10)
    DELL-Notebook (Intel Core i7 3520M Quad Core, 2,9GHz, 8GB RAM, NVIDIA Quadro K1500M, WIN 10 Home;
    EEP von 4 bis 17.1

    »Nach diesem Jahrhundert der großen Sprüche und schlimmen Illusionen hoffe ich auf eine Zeit, in der die großen Sprüche nichts mehr gelten, sondern nur noch das, was man wirklich und greifbar getan hat.«

    Gerhard Richter, 1999

  • Moin Cristopher,

    es ist immer interessant zu sehen, wie andere Aufgaben angehen und zu welchen Lösungen sie kommen.

    Und strukturiertes Programmieren ist Grundlage dafür, dass mann auch nach Wochen, Monaten oder Jahren seine Arbeiten versteht und nachvollziehen kann.

    Ich wäre auch sehr an Deinen Ausführungen interessiert und werde es gespannt verfolgen.

    Das Abo ist schon gesetzt.

    Man kann nur etwas dabei lernen.

    Danke schon mal für Deine Überlegungen und Deine Bemühungen, uns Deine Kenntnisse näher zu bringen.

    Gruß

    Holger

    NB: Toshiba Qosmio X770 3d, Inter(R) Core(TM) i7-2670QM CPU @ 2,2GHz, 8 GB, NVIDIA GeForce GTX 560M, Win 10 Home x64
    EEP 5.0, EEP 7 (intern 7.5), EEP 12 Expert, EEP13 Expert mit Patch 2, EEP14 Expert mit Patch 3, EEP 15 Expert + Patch 2, EEP 16 Expert + Patch 2, Hugo, PlanEx 300+ 310, 320 Modell-Konverter 1.3.7

  • Hallo zusammen,

    danke für die netten Kommentare.

    planst du, deine Ausführungen später in einem Dokument zusammenzufassen oder ist es sinnvoll, sich direkt selber ein pdf aus deinen Textteilen zu erstellen?

    Nee, das plane ich eigentlich nicht. Soooo gerne schreibe ich nämlich auch wieder nicht. Und als PDF wäre es mir zu sehr "Gesetz". Ich möchte Euch viel lieber zeigen, wie ich mich durchs Gestrüpp der Programmierung wühle. Eure Vorgehensweisen sind vielleicht andere. Lua lässt einem da auch sehr viele Freiheiten.

    Und deswegen stimme ich thoflo zu, dass man

    sich möglichst genau an diese (oder andere, aber Richtlinien!) zu halten.

    Es ist weniger wichtig, welche Richtlinien es sind, als dass es überhaupt eine Gleichmäßigkeit gibt. Denn nichts ist nerviger, als nach ein paar Wochen vor seinem eigenen Code zu sitzen und nicht mehr zu verstehen.

    Also: Macht Euch Euren eigenen Plan. Wenn Ihr keinen anderen vorzieht, darf es natürlich auch meiner sein. Und notiert Euch das, was Euch wichtig erscheint, und erschafft Euch damit Euer eigenes Nachschlagwerk. Ich kann so jedenfalls deutlich besser lernen als beispielsweise durch Videos, wo mich vielleicht ein guter Ansatz zwischen Minute 43:30 und 45:10 interessiert hätte —, wenn ich dort noch aufmerksam gewesen wäre.:ae_1:

    Aber auch gilt wieder: Jeder hat seinen eigenen Stil, auch im Lernen ...

    Gruß

    Christopher

    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)

  • Hallo Cristopher,

    ich finde es ebenfalls gut, deine Lua Module vorzustellen.

    Es wird auf alle Fälle dazu beitragen, LUA zu verstehen und Anregungen für eigene Programmierungen geben.

    Ich werde Deine Beiträge mit großem Interesse verfolgen.

    Viele Grüße Reiner

    Windows 10 Home 64 bit, Intel® Core™ i7-6700k Processor 4 x 4000 MHz,(4633 Mhz mit Wasserkühlung)

    Palit Geforce GTX 1060 /6 GB , DDR4-2133 16 GB) --->> und kein Zug ruckelt mehr<<---:ap_1:

    Fast alles ab EEP4

  • Ein Feld heißt immer _VERSION und enthält die Version des Moduls.

    Hallo Christopher :)

    beißt sich das nicht mit den Lexikalische Konventionen?

    Per Konvention sind Bezeichner, welche mit einem Unterstrich beginnen und im Folgenden durchgehend großgeschrieben werden (so wie _VERSION), reserviert und werden für interne globale Variablen von Lua verwendet.

    Gruß Ingo

  • Gestern am späten Abend hatte ich noch einen interessanten Gedankenaustausch mit Benny (BH2) . Danke dafür!

    In der Konsequenz überlege ich, ob ich nicht auf globale Variablen ganz verzichten sollte. Bisher verwende ich, wie geschrieben, je eine pro Modul.

    Wahrscheinlich kommt das aus meiner Anfangszeit mit Lua. Da war require für mich so etwas Ähnliches wie #include in C: Ich mache mir etwas verfügbar, was eigentlich woanders steht. Tatsächlich ist die Analogie aber nicht wirklich eine. Nur passiert das einem immer wieder, wenn man eine neue Programmiersprache lernt, dass man zunächst Analogien zum Bekannten sucht und vermeindlich findet.

    Benny (BH2) hat mich auf auf eine hervorragende Artikelreihe aufmerksam gemacht, die ich Euch als Empfehlung gerne weitergebe: A guide to authoring Lua modules

    Darin bin zum ersten Mal über den Begriff "monkey patching" gestolpert. Und ich denke, der Schreiber hat recht damit, dass man das nicht unnötig unterbinden sollte. Ein einfaches Beispiel für "monkey patching" wäre folgendes:

    Nehmen wir an, ich wollte etwas Zufallsgesteuertes machen. Dafür bietet Lua die Funktion math.random(). Ich hole mir also einen Zufallswert:

    Lua
    local value = math.random();

    Dann fange ich an, Fehler in meinem Programmcode zu suchen. Aber jedes Mal erhalte ich eine andere Zufallszahl, und es passiert etwas anderes in meinem Programm. Ein einfacher Weg ist jetzt, die Funktion übergangsweise durch etwas anderes zu ersetzen, das mir die Fehlersuche leichter macht:

    Lua
    math.random = function()
        return 0.5;
    end;

    Und schon liefert math.random() ganz stabil und immer 0.5. Meine Fehlersuche ist wesentlich erleichtert. Und das war "monkey patching".

    Zurück zum Thema:

    Wenn ich noch stärker auf globale Variablen verzichten will, und wenn ich "monkey patching" nicht verhindern will, muss ich meine Module ein bisschen umbauen.

    Eigentlich hatte ich gedacht, ich stelle hier meine Module vor, die sich in mehreren Anlagen als hilfreich und verlässlich herausgestellt haben. Ich wollte mich "nur" dazu zwingen, die Module noch ein bisschen aufzuräumen und aufzuhübschen, um sie Euch vorzustellen.

    Jetzt werde ich sie doch ein bisschen heftiger anfassen und vielleicht auch neue Fehler einbauen. Das ist mir aber lieber, weil ich Euch eher "gute" Programmier-Praxis zeigen will als erprobten, aber hässlichen Code.

    Zu _VERSION fragt schlingo :

    beißt sich das nicht mit den Lexikalische Konventionen?

    Meine Position dazu: _VERSION als globale Variable ist reserviert. Vielleicht könnte man sie auch mittels "monkey patching" ändern. :ao_1: Das will ich aber gar nicht.

    Ich habe in meinen Modulen ein Feld namens _VERSION, und das kollidiert mit nichts. So kann ich zum Beispiel prüfen, ob ich die richtige Version eines Moduls "am Wickel" habe.

    Mittlerweile sind mir noch ein paar Ergänzungen eingefallen, die ins Kapitel 1 hinein gehören. Aber das kommt im nächsten Beitrag.

    Gruß

    Christopher

    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)

  • _VERSION als Feld in der Tabelle wird übrigens auch im Guide to authoring Lua modules empfohlen.

    Zum "monkey patching" habe ich sogar noch ein auf EEP bezogenes Beispiel. Auch wenn mir dieser Begriff damals auch noch nicht bekannt war, habe ich mit meinem PrintToFile genau das gemacht: Die von EEP definierte print-Funktion überschrieben mit einer eigenen Variante, die den Text zusätzlich noch in eine Datei schreibt.

    Das "Kapitel 1" hat mir schon sehr gut gefallen :bp_1:Jetzt warte ich gespannt auf den ersten Code :aa_1: (oder andere Ergänzungen und Gedanken)

    Viele Grüße

    Benny

  • Die angekündigte Ergänzung zu Kapitel 1:

    1.9 Objekt-Orientierung

    Die Programmiersprachen, mit denen ich mich zuletzt beschäftigt hatte, gehören zu den objekt-orientierten. Außerdem ist Objekt-Orientierung zurzeit (immer noch?) das meistverbreitete Programmierparadigma. Mit Lua lässt sich das in Teilen nachbilden; Lua hat sogar dafür hilfreiche Konstrukte wie das Schlüsselwort self und den Operator :.

    Aus der Sicht von EEP erscheint es zunächst verlockend, einen Gleisabschnitt, einen Bahnübergang, eine Ampelkreuzung oder was auch immer als Objekt anzusehen, an dem dann Methoden hängen, die Informationen über dieses Objekt zurückgeben, oder die das Objekt manipulieren (verändern).

    Der Haken ist allerdings, dass Lua innen drin doch keine objekt-orientierte Programmiersprache ist. Früher oder später fängt man an, für ein objekt-orientiertes Erscheinungsbild ziemlich große Klimmzüge zu machen. Man kämpft sich durch Meta-Tabellen und erreicht doch nicht genau das, was man will. Zumindest erging es mir so.

    Zwei oder drei meiner Module waren einmal objekt-orientiert — bis ich für mich entschieden habe, dass ich mir damit keinen Gefallen tue. Also habe ich sie in der nächsten Version wieder ganz normal prozedural gebaut. Ihr werdet hier also keine Objekt-Orientierung mehr finden.

    1.10 Ästhetik in der Programmierung

    "Dreht er jetzt durch? Oder meint er das ernst? Es geht doch nicht um Kunst?!"

    Ja, ich meine das ernst. Ästhetik in einem Programm heißt für mich, dass ich es als schön, als elegant betrachte. Und mit etwas Schönem beschäftige ich mich lieber als mit etwas Hässlichem oder auch nur Gleichgültigem. Wenn das Stückchen Programm "schön" ist, fasse ich es gerne wieder an. Und ich achte darauf, dass es "schön" bleibt. Ich suche auch lieber Fehler in "schönen" Programmen als in hässlichen. Und möglichst sollte die Fehlerbehebung wieder "schön" sein. Meines Erachtens wird ein schönes Programm viel besser und gründlicher gepflegt und hält daher viel länger.

    Ein paar Kriterien, an denen ich "Schönheit" festmache:

    • Klare Aufteilung der Zuständigkeiten der einzelnen Module und der Funktionen darin
      Es wird keine neue Funktionalität einfach "irgendwohin" geschrieben, nur weil ich gerade eine andere Datei im Editor habe oder zu faul bin, ein neues Modul anzufangen.
    • Funktionen sind übersichtlich groß
      Wenn die Funktion nicht mehr vollständig in den sichtbaren Bereich des Editors passt (bei mir ca. 40 Zeilen), überlege ich ob ich sie nicht aufteilen sollte. Zugegebenermaßen halte ich mich nicht immer daran.
    • Eine Anweisung auf eine Zeile
      Die einzige Ausnahme von dieser Regel, die ich mir manchmal durchgehen lasse, ist: if simpleBedingung then machWasEinfaches; end;
    • Nicht zu viel verschachteln
      Natürlich gibt es auch bei mir mehrfach verschachtelte if-Abfragen, auch mit for-Schleifen etc. Aber das Ganze ist dann nicht auch noch in einer Funktion, die in einer Tabellen-Definition steckt, die selbst wieder in ...
      Der Grund ist einfach: Ich will immer wissen, wo ich bin. Und das sehe ich nicht, wenn wesentliche Teile des Kontexts ober- oder unterhalb des Bildschirm verschwinden.
      Daher komme ich mit meinen Einrückungen (auf die ich immer achte) niemals in die Nähe des rechten Bildschirmrands.
    • Keine Copy&Paste-Arien
      Wenn mehrere Zeilen untereinander ganz ähnlich aussehen und sich zum Beispiel nur in konkreten Zahlenwerten unterscheiden, frage ich mich, ob ich hier nicht eher eine Tabelle brauche, die ich in einer Schleife abarbeiten kann.

    Es gibt sicher noch viel mehr Kriterien, aber Ihr seht schon, wo "der Hase lang läuft".

    1.11 Statefulness oder Statelessness

    Oh je, diese alte Grundsatzdiskussion! Allein diese Wörter!

    "Stateful" heißt auf deutsch "zustandsbehaftet". Es geht (vereinfacht gesagt) darum, ob ein Modul ein Gedächtnis hat oder haben darf. Wenn es zustandslos ist, wird ein Funktionsaufruf mit denselben Parametern auch immer dasselbe Ergebnis haben. Wenn es einen Zustand speichert, kann die Verarbeitung oder der Rückgabewert jedesmal anders ausfallen, auch wenn die Funktion mit denselben Parametern aufgerufen wird.

    Es gibt viele Argumente für die Zustandslosigkeit und man kann sie vermeiden, indem man den Kontext als weiteren Parameter mitgibt. Zustandslosigkeit wird wichtig, wenn ich die Laufzeitumgebung nicht kenne, oder wenn die Funktion möglich allgemeingültig einsetzbar sein soll. Enrique García Cota (kiko.to) empfiehlt ganz klar zustandslose Module.

    Ich pfeife darauf! Ich kenne die Laufzeitumgebung, nämlich EEP, und ich möchte meinen Lua-Funktionen nur dort einsetzen. Ich will gerade, das meine Module ein Gedächtnis haben!

    Da weiß der Bahnübergang, ob er frei ist, oder die Drehscheibe, wie sie steht, oder das Gleis, ob es besetzt ist, oder der Zug, ob er vorwärts oder rückwärts fuhr, bevor ihn das Signal angehalten hat.

    Wenn Ihr also Verfechter der Zustandslosigkeit seid, werde ich Euch massiv enttäuschen.


    Und nächstes Mal geht's wirklich los. Zunächst ganz einfach. Versprochen.

    Gruß

    Christopher

    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)

  • Hallo Michael (MH3) und an Alle, die auf ein PDF warten,

    spricht etwas dagegen, den Text hier aus dem Thread in ein Textprogramm zu kopieren und dann als PDF zu speichern? Ich werde das auf jeden Fall so machen.

    Gruß

    Paul

    Gruß

    Paul

    EEP Daten

    EEP17.2 Patch1 mit Win10, Ryzen5 2600, 32GB RAM und RTX3060 12GB VideoRAM

  • Ich bin gespannt, was Du uns präsentieren wirst. Was den Programmierstil betrifft, kann ich einiges bei mir wiederfinden, aber ganz so abstrakt und "programmierphilosphisch" gehe ich bei meiner Anlagensteuerung nicht an die Arbeit.

    Vieleicht hat es ja auch mit der (gewollt) fehlenden Modularität meines Gesamtkunstwerks zu tun. Um eine durchgehendes Konzept vom Fahrplantakt für Zugrouten bis zum korrekt gestellten Zusatzsignal zu erreichen, muss ich ein enormes Datengebäude aufbauen, auf das dann alle Funktionen zugreifen können. Alleine die Fahrstraßendefinitionen für die drei nicht allzu großen fahrstraßengesteuerten Bahnhöfe meiner Testanlage nehmen über 5000 Zeilen in Anspruch (wobei ich allerdings der besseren Übersichtlichkeit wegen viel mit Leerzeilen und Umbrüchen schreibe) und haben 6 Verschachtelungsebenen. Wenn diese Tabellen abgesucht werden müssen, geht das natürlich nicht ohne mehrfach verschachtelte Schleifen und If-Abfragen. Aber dafür sind die Funktionen, die diese Arbeit erledigen, dann auch wirklich relativ überschaubar.

    Einmal editiert, zuletzt von klioli (12. Dezember 2018 um 08:50)