Alexander Kornrumpf hat geschrieben: ↑05.04.2022, 13:26
Interessante Betrachtungsweise, ich hätte behauptet, geschachtelte ifs sind in den meisten Fällen leichter zu lesen als zusammengesetzte booleansche Ausdrücke
"geschachtelte ifs" vs. "zusammengesetzte boolesche Ausdrücke" wäre für das Paradigma ja kein "entweder - oder", sondern ein "weder - noch" :)
Wenn die Ausdrücke nur noch aus kommutativen And-Ketten bestehen dürfen, sind sie ja völlig ohne nachzudenken zu erfassen. Auch wenn Redundanz entsteht, sobald man das komplett ausschreibt. (Die Redundanz wird in seiner Tabelle u.a. deutlich wo in Spalte 2,3,4 ein gemeinsames "false" steht.)
- fig5.jpg (59.93 KiB) 2115 mal betrachtet
Diese Tab. aus dem Video wäre ja spaltenweise interpretiert und in Code ausgedrückt:
Code: Alles auswählen
if(a==true) x=1;
if(a==false && b==true) x=2;
if(a==false && c==true) x=2;
if(a==false && b==true && c==true) x=3;
Die Frage "Wann ist x=3?" lässt sich mit diesem Code auf einen Blick beantworten, im Vergleich zum im Video davor gezeigten Codesnippet.
Sollten aber in den Vergleichen und Zuweisungen Funktion angestoßen werden, kann Redundanz nicht nur unnötig, sondern gefährlich werden. Daher sage ich ganz deutlich: Ich bin kein Verfechter, dieses Paradigma überall 1:1 im Code anzuwenden, es gibt eher mehr Fälle wo das für mich absolut keinen Sinn macht.
Aber für eine State-Machine, die erst nach einem kompletten Logik-Step Dinge auslöst, wäre das Verhalten ok.
Also, gerade bei Storyentwicklung, wo sich mit der Zeit gerne mal wirre Konstrukte wie "Wenn a und b, aber nicht c, oder b und c ohne a, dafür aber mindestens 1 d, letzteres aber nur wenn, ..." bilden - was umgangssprachlich mit Kontext dann sogar noch verständlich wäre (also in einem Roman so abgedruckt und verstanden werden könnte) - da tut man sich aus meiner Erfahrung keinen Gefallen damit, zu versuchen, den Text 1:1 mit geklammerten bool-Ausdrücken und else-Verschachtelungen in einem Abwasch nachzuvollziehen.
Aber genau sowas entsteht ja oft, wenn man später NOCH eine Bedingung an einer bestimmten logischen Stelle dieser Kette einarbeiten möchte - und es kann passieren, dass dabei versehentlich ein bisher richtiger else-Zweig mit falschen Bedingungen entsteht, u.U. auch weit weg von der Änderungsstelle. Oder dass man die neue Bedingung eigentlich an mehreren Stellen einbauen muss, damit sie alle bisherigen Fälle auch wieder richtig erfasst. Oder man bemerkt, dass man den Baum durch die neue Bedingung nun ganz anders viel eleganter zusammenfassen könnte. Usw.
Wenn man solche Aufgaben stattdessen stur in reine AND-Ketten zerlegt, ohne else, können solche Änderungen deutlich leichter und besser wartbar werden. Ob man das später wieder zu eleganterem Code zusammenfasst, steht natürlich frei.
Alexander Kornrumpf hat geschrieben: ↑05.04.2022, 13:26
Sich zu "merken" in welchem Fall man gerade ist setzt schließlich voraus, dass man den Fall mental mit irgendeinem Konzept verbinden kann (sich also gerade _nicht_ ohne Kontext den Wert einzelner booleans merken muss) und das macht sein künstliches a, b, c Beispiel natürlich absichtlich kaputt.
Magst du ein Codeschnipsel aus Weentown zeigen
Ja, das abstrakte Beispiel am Videoanfang demonstriert nicht gerade die Praxis.
Paar Beispiele von mir wie ich das Paradigma genau nutze. Könnte zwar, aber stammt nicht 1:1 aus Weentown, denn das wäre ohne Kontext für Außenstehende wahrscheinlich noch unverständlicher :D
Erstmal zu meiner Syntax. Ich habe dafür eine sehr sehr simple String-Statemachine genutzt, eigentlich nur eine String-String-Hashtable. Ich tippe aber nicht jedesmal
Code: Alles auswählen
if(states["door"]=="open" and states["key"]=="inventory")states["message"]="dummer comment"
sondern über eine Helferfunktion abgekürzt nur
Code: Alles auswählen
exec("door?open;key?inventory;message:dummer comment");
"?" ist State-Vergleich, ":" ist Zuweisung, alle Zuweisungen in einer Zeile werden genau und nur dann durchgeführt, wenn alle zu prüfenden States der Zeile zutreffen (also ausnahmslos AND-Ketten). Vergleich ohne Zuweisung ist ebenso möglich wie Zuweisong ohne Vergleich für einen State.
Es ist außerdem keine dynamische Statemachine, die sofort irgendwelche Aktionsketten oder Events bei State-Änderung auslöst.
Damit würde das o.g. Beispiel so aussehen:
Code: Alles auswählen
exec("a?true;x:1");
exec("a?false;b?true;x:2");
exec("a?false;c?true;x:2");
exec("a?false;b?true;c?true;x:3");
Kann man jetzt anfangen händisch zusammenzufassen. Könnte man auch über einen (noch zu programmierenden) Vorinterpreter schicken der das optimiert. Kann man aber auch einfach so lassen, wenn der Code nicht zeitkritisch ist. Bei mir wird er z.B. nur 1x ausgelöst, wenn der Spieler ein Kommando wie "gehe Tür" auslöst.
Ich hätte in der Praxis evtl. noch die 3 "a?false" mit einem übergeordneten regulären if zu einem Block zusammengefasst, weil das die Lesbarkeit erhöht, mehr nicht.
Ein typischeres Praxisbeispiel, wie so eine hart gecodete Story bei mir aussieht:
Code: Alles auswählen
//Wenn die Tür verschlossen ist und man den Schlüssel oder das Brecheisen hat, kann man die Tür damit öffnen; der jeweilige Gegenstand verschwindet bei Erfolg aus dem Inventar.
exec("door?closed:open;command?use key with door:done;key?inv:kill;msg:I opened the door with the key.");
exec("door?closed:open;command?use crowbar with door:done;crowbar?inv:kill;msg:I opened the door with the crowbar.");
Erste Zeile im Klartext: WENN (door==closed UND command==use key with door UND key==inv) DANN door=open, command=done, key=kill, msg=...
(Da die Syntax ja aus meiner Feder stammt, ist die für mich gut lesbar, auch wenn sie für euch jetzt sicher verwirrend erscheint.)
Was ich da in meinem Code notiere, ist also eigentlich nichts anderes als eine einfache Decision-Table, um 90 Grad gedreht.
Chromanoid hat geschrieben: ↑05.04.2022, 21:31
Sind das aber nicht genau Fälle, die man eigentlich nicht als Teil des Kompilats haben will? Das will man doch eigentlich im besten Fall während des Spielens ändern? Versteht mich nicht falsch, ich finde Entscheidungstabellen echt cool, aber eben eher als "modifizierbare Geschäftsregel", die wie Scripte eher als Daten zu behandeln sind. Natürlich verschwimmt die Trennung je nach Sprache etwas.
Genau. Gerade in meinem Anwendungsfall wird das ja überdeutlich, ich könnte ja jederzeit schreiben: foreach(string line in file)exec(line) und hätte damit die komplette Story raus aus dem Kompilat. Was man für eine Engine dann auch so machen würde.