Ich schreibe eine Golfsprache.
Schlagen Sie Variablen, Stapel, Bänder, Register usw. zur Speicherung in einer Code-Golf-Sprache vor? Was ist mit impliziten Eingaben?
Grobe Definitionen:
- Eine Variable ist einfach ein Name (normalerweise ein Zeichen in Golfsprachen), dem ein Wert zugewiesen und später über diesen Namen abgerufen werden kann.
- Ein Register ist wie eine Variable, verfügt jedoch über eigene (in der Regel Einzelbyte-) Befehle zum Festlegen / Abrufen des Werts.
- Ein Stapel ist ein Array / eine Liste von Werten variabler Länge, wobei die zuletzt hinzugefügten Werte (die Werte "oben") diejenigen sind, die geändert werden.
- Eine Warteschlange ist wie ein Stapel, mit der Ausnahme, dass die Werte " unten " geändert werden.
- Ein Band ist ein statisches Array / eine Liste von Werten, bei denen jeder Wert einen Index hat. Der Hauptunterschied zwischen einem Stapel und einem Band besteht darin, dass die Werte auf einem Band direkt geändert werden.
Ich würde mich freuen, die Vor- und Nachteile jeder Option für verschiedene Szenarien und insgesamt zu kennen. Bitte vermeiden Sie Meinungen und begründete Backup-Aussagen.
code-golf
tips
language-design
golfing-language
programmer5000
quelle
quelle
Antworten:
Bei allen Speichertypen wird etwas an einem Punkt gespeichert und später abgerufen. Um dies in nur einer Operation zu tun, sollten Sie entweder automatisch speichern oder abrufen und die Position des gespeicherten Werts in der anderen Operation angeben.
Das heißt, Sie können zum expliziten Speichern einen Operator erstellen, um den n-ten berechneten Wert vor dieser Operation abzurufen, oder den aktuellen Wert nach n Operationen zurücksetzen. Alternativ können Sie die absolute Position vom Start des Programms an verwenden oder weitere Aktionen ausführen, z. B. das automatische Entfernen einiger Elemente nach bestimmten Vorgängen (z. B. in einem Stapel). Sie können auch mehrere Operatoren erstellen und mit oder ohne diese automatischen Vorgänge von verschiedenen Kopien des Speichers abrufen. Und Sie sollten versuchen, die maximale Anzahl, die für die Angabe in den Vorgängen erforderlich ist, angemessen klein zu halten, damit Sie für jede Nummer einen Operator zuweisen können.
In den meisten Fällen benötigen Sie jedoch nicht einmal einen Operator und die Sprache erledigt dies implizit. In diesem Fall müssen Sie ein standardisierteres Modell wie Stapel oder Warteschlangen in Betracht ziehen. Das bisher erfolgreichste schien die stillschweigende Programmierung zu sein, bei der nicht einmal die direkte Speicherung erwähnt wird.
Wenn Sie ein neues solches Modell entwerfen möchten, können Sie versuchen, die Auswertungen als Tag zu erweitern und sich einen Standardtag vorzustellen, wenn nichts anderes angegeben ist. Die Standardeinstellung ist höchstwahrscheinlich nur ein Baum, mit der Ausnahme, dass mehrere Blätter mit derselben Eingabe verknüpft sein können. Sie können beispielsweise eine Warteschlange für einen ausgeglichenen Baum oder einen Stapel für einen tiefen Baum verwenden, bei dem die Blätter meist konstant sind, oder Jelly für einen tiefen Baum, bei dem die Blätter meist Kopien der Eingabe sind.
Beachten Sie jedoch, dass Sie die Form eines Binärbaums in nur 2 Bits pro Operator codieren können. Wenn Ihre Sprache also weniger als 64 Operatoren enthält, können Sie die herkömmlichen Modelle ignorieren und einfach den gesamten Baum in die Ersatzbits kodieren (nennen Sie sie "Combine_parent" und "Below_leaf"). Selbst wenn es mehr Operatoren gibt, können Sie einen recht guten Standardwert (wie das Modell von Jelly) und drei Modifikatoren festlegen, um ihn zu ändern.
Sie könnten das gleiche Modell für die implizite und explizite Speicherung verwenden, müssen dies jedoch nicht. Beispielsweise könnten Sie einen Stapel für die implizite Speicherung verwenden, jedoch keine Elemente im expliziten Speicher (oder in einem anderen expliziten Speicher zusätzlich zum impliziten Speicher) einfügen. Es ist wahrscheinlich, dass es in der endgültigen Dokumentation nicht als Stapel bezeichnet wird, aber Sie haben die Idee.
Als Referenz ist die Größe der perfekten Kodierung eines Binärbaums der Logarithmus der katalanischen Zahlen . Und die Größe der perfekten Kodierung eines "binären" Tags ist der Logarithmus von A082161 , aber offensichtlich unpraktisch. Dies setzt voraus, dass ein Operator mit unterschiedlicher Argumentreihenfolge zwei verschiedene Operatoren hat und ein weiteres Bit hinzufügt, wenn dies nicht der Fall ist.
Manchmal möchten Sie vielleicht immer noch Variablen für die Schleifen. Möglicherweise können die Schleifen auf andere Weise neu geschrieben werden. Aber wenn Sie es wirklich brauchen, verwenden Sie kein 1-Byte-Konstrukt zusätzlich zu einem Namen, um die Variable zu definieren. Wenn Sie nicht nur die vorinitialisierten Werte verwenden, ist es normalerweise effizienter, ein 1-Bit-Flag zu verwenden, um anzugeben, ob Sie diese Variable lesen oder schreiben.
quelle
Ich schlage sie alle vor!
Im Ernst, sie sind alle manchmal nützlich, und je mehr, desto besser! Implizite Eingaben sind niemals schlecht. Sie müssen lediglich ein Flag verwenden, um sie zu deaktivieren. Variablen sind hilfreich, damit sie nicht auf einem Stapel oder Band gefunden werden müssen. Gleiches gilt für Register. Stapel sind hilfreich zum Speichern von Daten, ebenso wie Bänder. Ich empfehle, mehrere zu implementieren, beispielsweise einen Stack und Register, oder einen Stack und Variablen wie GolfScript. Wenn Sie jede Funktion zu einem Byte machen können, wird Ihre Sprache beim Golfen wahrscheinlich effektiv sein, da Sie die Vorteile der einzelnen Funktionen nutzen können.
Ein Beispiel:
Beispielcode in GolfScript (mit impliziter Eingabe):
Mit Variablen (ich weiß, es ist länger, es muss nur nicht die Plätze auf dem Stapel tauschen):
Überladungen
Eine andere Sache, die hilfreich sein kann, ist Überlastungen. Wenn Sie beispielsweise eine Funktion zum Speichern von Variablen haben, könnte diese als Monade (Funktion mit einem Eingang; ich bin mir nicht sicher, ob der Begriff außerhalb von J / K / APL verwendet wird) verwendet werden, um sie dem Stapel oder dem Band hinzuzufügen.
Beispiel:
quelle
Ich würde vorschlagen, einen schnell verwendbaren Speicher (von dem gegebenen - Band, Warteschlange, Stapel) und einen permanenten Speicher (Variablen, Register) zu haben, damit die Dinge nicht im Wege stehen, während das Programm etwas anderes tut. Ich würde sagen, viel mehr würde selten etwas geben und mehr Zeichen für mehr 1-Byte-Anweisungen frei lassen.
Nach den gegebenen Definitionen sind die, von denen ich denke, dass sie am besten funktionieren, ein Stapel und Register.
Ein Stapel, weil ein Band Funktionen verwenden muss, um ein neues Objekt darin zu speichern, während ein Stapel einfache Push- und Pop-Funktionen haben sollte (normalerweise in den Befehlen enthalten). Register, weil sie im Vergleich zu Variablen normalerweise weniger Bytes benötigen und wenn Sie mehr als 2-4 verschiedene Dinge für etwas speichern müssen, machen Sie etwas falsch.
Beschränken Sie die Funktionen nicht nur auf das, was ihre Namen oder Definitionen vermuten lassen - einige Funktionen wie
put the 1st thing of the stack on top of the stack
können definitiv helfen.quelle
Grundsätzlich gibt es zwei Arten von Speicher, die unterschiedlich behandelt werden müssen. Zugriff auf kürzlich generierte Werte und / oder die Eingaben; und langfristige Lagerung.
Für die Langzeitspeicherung scheinen Variablen am besten zu funktionieren. Alles mit einer begrenzten Anzahl von Optionen lässt sich nicht skalieren (obwohl Sprachen mit dieser Eigenschaft, wie Jelly, auch bei mittelgroßen Aufgaben ziemlich gut funktionieren). Es ist jedoch meistens nicht erforderlich, Variablennamen anzugeben, wenn die Variable gespeichert wird. Sie müssen lediglich einen Befehl zum Speichern eines Werts in der Variablen ausführen und die Namen nach einem vorhersagbaren Muster automatisch generieren, sodass das Speichern und Abrufen des Werts in einfachen Fällen jeweils ein Befehl sein kann. (Sie könnten beispielsweise Befehle zum Wiederherstellen der zuletzt zugewiesenen Variablen, der zuletzt zweiten, der zuletzt dritten usw. bis zu einer kleinen festen Zahl sowie eines allgemeinen Befehls haben, der ein Argument angenommen hat.)
Für die kurzfristige Speicherung soll so viel wie möglich implizit sein. Fast alle Golfsprachen, die ich gesehen habe, verbinden standardmäßig die Ausgabe eines Befehls mit einer Eingabe des nächsten. Die genaue Art und Weise, in der dies geschieht, ist von Sprache zu Sprache unterschiedlich, kommt aber normalerweise auf dasselbe hinaus. Interessanter ist jedoch die zweite Eingabe für einen Befehl mit zwei Eingängen. Die Entnahme von einem Stapel funktioniert gut, wenn Werte nur einmal verwendet werden, die Skalierung bei der Wiederverwendung von Werten jedoch nicht gut ist. (Versuchen Sie, Stapelmanipulationsprimitive zu vermeiden. Wenn Sie auf deren Verwendung zurückgreifen müssen, verschwenden Sie viele Bytes.) Alternativ können Sie durch die Verwendung von Benutzereingaben oder einer kürzlich zugewiesenen Variablen als implizites zweites Argument einige Byte einsparen einfache Programme, obwohl Sie
In einer Golf-Sprache, an der ich gerade arbeite, verwende ich eine Kombination aus einem sehr günstigen Mechanismus (ein einzelnes Bit ), um die Form des Analysebaums anzugeben, wobei automatisch fehlende Argumente aus Benutzereingaben und ein Checkpointing eingefügt werden Ansatz, der es ermöglicht, den Standard für fehlende Argumente auf etwas anderes festzulegen (plus viele Sonderfälle). Irgendwann werde ich wahrscheinlich Variablen hinzufügen, um Informationen über größere Entfernungen entlang des Programms zu kommunizieren.
quelle
Ich schlage ein Band und ein Register vor.
Ich bevorzuge Bänder gegenüber Stapeln, da Bänder tendenziell weniger Elemente enthalten, was die Manipulation erleichtert. Die Möglichkeit, Elemente auf dem Band im Register zu platzieren und umgekehrt, sorgt für einen einfachen, kurzen Code.
quelle