Prindeal (ausgesprochen prin-dee-al ) ist eine neue esoterische Programmiersprache, die nur vier Befehle enthält: pr int , in crement , de crement und al ias . Trotz seines Minimalismus können komplexe mathematische Operationen in Prindeal durchgeführt werden, indem die vier Befehle geschickt kombiniert werden.
Ihre Aufgabe bei dieser Code-Golf- Herausforderung ist es, das kürzeste Programm zu schreiben, das Prindeal-Code ausführen kann.
Die Spezifikation ist lang, aber ich habe versucht, sie so deutlich wie möglich zu machen, und ich glaube, wenn Sie sich die Mühe machen, Prindeal zu lernen, werden Sie feststellen, dass es ziemlich elegant ist!
Prindeal interpretieren
Vorverarbeitung
Bevor ein Prindeal-Programm interpretiert werden kann, müssen diese Dinge in dieser Reihenfolge entfernt werden:
- Alles, was nach einem
#
Zeichen bis zum Ende der Zeile steht, ist eingeschaltet, plus das#
Selbst. (Dies sind Kommentare.) - Leerzeichen in jeder Zeile nachstellen.
- Völlig leere Zeilen.
Zum Beispiel das Prindeal-Programm
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
würde vorverarbeitet werden
p cat
p dog
Ab hier gehen wir davon aus, dass dieser Vorverarbeitungsschritt abgeschlossen ist.
Variablen
Wir müssen schnell Variablen definieren, bevor wir zeigen, wie sie verwendet werden.
Variablen (und Verweise auf Variablen) werden an die Argumente von Prindeal-Befehlen übergeben. Variablen sind immer global , sodass Änderungen an einer Variablen, unabhängig davon, wo sie auftreten, überall wiedergegeben werden.
Jede Variable enthält eine nicht negative Ganzzahl mit beliebiger Genauigkeit (0, 1, 2, 3, ...). Variablen müssen nicht vorinitialisiert werden - sie beginnen immer mit dem Wert 0, wenn sie zum ersten Mal verwendet oder aufgerufen werden.
Ein Variablenname kann eine beliebige nicht leere Zeichenfolge aus alphanumerischen Zeichen und Unterstrichen sein, die nicht mit einer Ziffer beginnt - [a-zA-Z_][0-9a-zA-Z_]*
in Regex . Es wird zwischen Groß- und Kleinschreibung unterschieden, spiny_lumpsuck3r
und Spiny_lumpsuck3r
es handelt sich um verschiedene Variablen.
Ausführung
Prindeal ist eine zwingende Programmiersprache. Wenn ein Prindeal-Programm ausgeführt wird, werden seine Anweisungen der Reihe nach von oben nach unten ausgeführt und das Programm endet.
Jede nicht eingerückte Zeile in einem Prindeal-Programm ist eine Anweisung, die die Ausführung eines einzelnen Befehls beinhaltet, der Argumente annehmen kann oder nicht.
Indented Linien treten erst nach Alias - Befehle. Insbesondere treten genau drei Zeilen, die mit einzelnen Leerzeichen eingerückt sind, nach jedem Alias- Befehl auf und werden als Teil davon betrachtet. So alias Aussagen sind wirklich lange vier Linien. (Es kann sich um eine Zeile handeln, vier sind lediglich lesbarer.)
Non- Alias- Anweisungen
Mit Ausnahme des Alias hat jede Anweisung in einem Prindeal-Programm die Form:
[command name] [argument 1] [argument 2] [argument 3] ...
Es kann eine beliebige Anzahl von Argumenten geben (einschließlich überhaupt keiner). Jedes Argument ist immer eine Variable oder (wie wir bei der Beschreibung des Alias sehen werden ) ein Verweis auf eine Variable .
Sobald die Ausführung abgeschlossen ist, wird jede Anweisung als fehlgeschlagen oder erfolgreich markiert, je nachdem, ob Fehler aufgetreten sind oder nicht. (Dies ist nur dann wirklich wichtig, wenn wir uns mit der Verwendung von Alias befassen .)
Die integrierten Funktionen print , increment und decrement sind Anweisungen mit der obigen Form. Folgendes machen sie:
print hat einen Befehlsnamen
p
und akzeptiert ein Argument. Es gibt den Namen der übergebenen Variablen und ihren Wert (in Dezimalzahl) aus, getrennt durch "=", gefolgt von einer neuen Zeile. Es wird immer als Erfolg gewertet .Zum Beispiel das Prindeal-Programm
p _MyVariable_321 p screaming_hairy_armadillo
würde ausgeben
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
weil alle Variablen bei 0 beginnen. (Die Leerzeichen vor und nach dem Gleichheitszeichen sind erforderlich.)
increment hat den Befehlsnamen
i
und nimmt ein Argument entgegen. Es erhöht den Wert der übergebenen Variablen um 1. Es wird immer als Erfolg gewertet.Zum Beispiel das Programm
i alpaca p alpaca i alpaca p alpaca
würde ausgeben
alpaca = 1 alpaca = 2
Beachten Sie, wie
alpaca
von 0 auf 1 inkrementiert wurde, obwohl noch nie zuvor darauf zugegriffen wurde.decrement hat einen Befehlsnamen
d
und benötigt ein Argument. Wenn die Variablen übergeben ist ungleich Null sein Wert um 1 verringert wird und die Anweisung wird als markiert Erfolg . Wenn die übergebene Variable 0 ist, wird nichts unternommen und die Anweisung als fehlerhaft gekennzeichnet .Zum Beispiel das Programm
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
würde ausgeben
malamute = 1 malamute = 0 malamute = 0 akita = 0
Beachten Sie, dass das Dekrementieren einer Variablen mit dem Wert 0 die einzige Möglichkeit ist, einen Fehler zu erzeugen .
Die Alias- Anweisung und die Alias-Befehle
Der Alias- Befehl hat eine spezielle Syntax und ist am leistungsfähigsten, da er zum Definieren neuer Befehle verwendet werden kann. Der Name des Aliasbefehls lautet a
und eine Aliasanweisung hat die Form:
a [name of new command]
[statement A]
[statement B]
[statement C]
Wobei jedes [statement X]
eine Nicht- Alias- Anweisung darstellt, dh etwas mit der Form [command name] [argument 1] [argument 2] [argument 3] ...
.
Der Name des Befehls mit Alias [name of new command]
kann eine beliebige nicht leere Zeichenfolge aus alphanumerischen Zeichen und Unterstrichen sein, die nicht mit einer Ziffer beginnt - [a-zA-Z_][0-9a-zA-Z_]*
in Regex.
(Dies ist der gleiche Satz von Namen wie Variablen, aber Alias- Befehle und Variablen werden an verschiedenen Stellen unterschiedlich verwendet . Eine Variable kann den gleichen Namen wie ein Befehl haben, ohne dass dies negative Folgen hat.)
Wenn eine Alias- Anweisung ausgeführt wird, wird neben den ursprünglichen vier p
i
d
a
Befehlen ein neuer Befehl hinzugefügt . Der neue Befehl kann [command name]
wie jeder andere Nicht- Alias- Befehl als in-Anweisung verwendet und mit Argumenten aufgerufen werden .
Wenn eine Anweisung mit einem Alias-Befehlsnamen ausgeführt wird, werden genau zwei weitere Anweisungen aus der ursprünglichen Alias- Anweisung ausgeführt:
[statement A]
wird immer ausgeführt[statement B]
wird ausgeführt, wenn[statement A]
ein Erfolg war[statement C]
wird ausgeführt, wenn[statement A]
ein Fehler aufgetreten ist
Anweisungen A, B und C werden immer träge ausgeführt , dh sie werden sofort zum Zeitpunkt der Ausführung ausgewertet.
Wenn Sie fertig ausgeführt wird , wird der Alias - Befehl mit dem gleichen gekennzeichnet Erfolg oder Versagen Flagge als Aussage B oder C, je nachdem , welche ausgeführt wurde . ( Alias- Anweisungen selbst müssen nicht markiert werden, da sie nicht in sich selbst vorkommen können.)
Alias Beispiel 1
Angenommen, wir möchten einen neuen Befehl, der die Variable
frog
zweimal inkrementiert . Diese Alias-Anweisung erreicht es:a increment_frog_twice i frog i frog d frog
Anweisung A (
i frog
) wird immer ausgeführt und immer als Erfolg gekennzeichnet, daher wird auch Anweisung B (i frog
) ausgeführt und die Variablefrog
wird daher um 2 erhöht. Derincrement_frog_twice
Befehl wird immer als Erfolg gekennzeichnet, da Anweisung B immer ausgeführt wird und B immer a ist erfolg . Anweisung C (d frog
) wird niemals ausgeführt.Also die Ausgabe an
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
wäre
frog = 0 frog = 2
Wir können dieses Beispiel so verallgemeinern, dass jede Variable zweimal inkrementiert werden kann, indem Sie dem Alias-Befehl ein Argument geben.
In einer Alias- Anweisung stehen die positiven Ganzzahlen 1, 2, 3 usw. für die Argumente 1, 2, 3 usw., die an den Alias-Befehl übergeben werden. (Diese Argumente können einfache Variablen oder Verweise auf Variablen selbst sein.) Diese Zahlen können nur in den Argumenten von Anweisung A, B und C in einer Alias- Anweisung vorkommen. Es macht keinen Sinn, wenn sie woanders auftauchen.
Alias Beispiel 2
Dies verallgemeinert das letzte Beispiel: Jede Variable, an die übergeben
increment_twice
wird, wird um 2 inkrementiert, da1
es sich um einen Verweis auf das erste übergebene Argument handelt:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
Die Ausgabe dieses Programms wäre
toad = 0 toad = 2
Wir könnten dann einen anderen Befehl aliasen, der zwei Argumente aufnimmt und
increment_twice
beide aufruft :a increment_twice i 1 i 1 d 1 #never reached a increment_both_twice increment_twice 1 increment_twice 2 d 1 #never reached increment_both_twice platypus duck p platypus p duck
Die Ausgabe hier wäre
platypus = 2 duck = 2
Es ist wichtig zu wissen, dass Alias-Befehle rekursiv sein können, da hier ihre wahre Kraft liegt. Zum Beispiel können wir einen Befehl erstellen, der jede übergebene Variable auf 0 setzt:
Alias Beispiel 3
Der
set_to_zero
Befehl akzeptiert ein Argument und setzt seine Variable auf 0. Wenn er fertig ist, wird er als erfolgreich markiert :a set_to_zero d 1 set_to_zero 1 i _dummy_ i oryx i oryx i oryx p oryx set_to_zero oryx p oryx
Die Ausgabe dieses Programms wäre
oryx = 3 oryx = 0
Was passiert ist, dass, wenn ausgeführt
set_to_zero oryx
wird,d 1
erfolgreichoryx
von 3 auf 2 dekrementiert wird , dannset_to_zero 1
aufgerufen wird, was dasselbe ist wie einset_to_zero oryx
erneuter Aufruf . Der Prozess wird wiederholt, bisd 1
ein Fehler aufgetreten ist. Die Rekursion wird gestoppt und die_dummy_
Variable erhöht , sodass ein Erfolg erzielt wird.
Herausforderung
Schreiben Sie ein Programm, das Prindeal-Code genau wie oben beschrieben ausführen kann. Übernehmen Sie den Prindeal-Code über stdin, die Befehlszeile oder als Textdatei. Drucken Sie die Ausgabe des Prindeal-Programms auf stdout oder die nächstgelegene Alternative Ihrer Sprache.
Alternativ können Sie eine Funktion schreiben, die den Code als Zeichenfolge aufnimmt und die Ausgabezeichenfolge ausgibt oder zurückgibt.
Darüber hinaus können Sie davon ausgehen, dass:
- Der eingegebene Prindeal-Code enthält nur Zeilenumbrüche und druckbare ASCII-Zeichen und endet (optional) mit einer Leerzeile.
- Der eingegebene Code ist gültig Prindeal - wohlgeformt und syntaktisch korrekt.
- Das Ausführen des Codes führt weder zu Endlosschleifen noch zu ungültigen Verweisen auf Befehle, die nicht definiert wurden, oder auf Argumente, die nicht angegeben wurden.
- Die Befehlsnamen
p
,i
,d
, unda
werden nie über aliased werden. (Sie können nicht davon ausgehen, dass Variablen diese Namen nicht haben.)
Außerdem spielt es keine Rolle, ob Ihre Variablenwerte wirklich Ganzzahlen mit willkürlicher Genauigkeit sind, da nur Zahlen mit weniger als etwa 1000 getestet werden. Es ist auch in Ordnung, wenn Ihre Sprache über Rekursionsbeschränkungen (wie Python ) verfügt, auf die komplexere Prindeal-Programme möglicherweise stoßen, solange das folgende Testprogramm funktioniert.
Testprogramm
Hier ist ein großes Prindeal-Programm, das die Operationen Addition, Multiplikation und Exponentiation durch die Verwendung von Dummy-Variablen (beginnend mit _
Konvention) und vielen Helfer-Aliasen aufbaut :
#Command Definitions:
a s #flag as a success
i _
d _
d _
a f #flag as a failure
d _
d _
d _
a z #1 = zero
d 1
z 1
s
a n #1 = one
z 1
i 1
s
a move #2 += 1, 1 = zero
moveH 1 2
move 1 2
s
a moveH #move helper
d 1
i 2
f
a dupe #2 += 1, 3 += 1, 1 = zero
dupeH1 1 2 3
dupe 1 2 3
s
a dupeH1 #dupe helper
d 1
dupeH2 2 3
f
a dupeH2 #dupe helper
i 1
i 2
s
a copy #2 = 1
z 2
copyH 1 2
s
a copyH #copy helper
dupe 1 2 _copy
move _copy 1
s
a addTo #1 += 2
copy 2 _add
#testing comments #
move _add 1#in weird places # just because #
s
#it's a g##d idea
###
a add #1 = 2 + 3
#its a good idea
z 1
addH 1 2 3
s
##
#
a addH #add helper
#this is a comment
addTo 1 2 #as is this
addTo 1 3
s
a mul #1 = 2 * 3
mulH1 1 2
mulH2 1 3
s
a mulH1 #mul helper
z 1
copy 2 _mul
s
a mulH2 #mul helper
mulH3 1 2
mulH2 1 2
s
a mulH3 #mul helper
d _mul
addTo 1 2
f
a mulBy #1 *= 2
mul _mulBy 1 2
copy _mulBy 1
s
a pow #1 = 2^3
powH1 1 3
powH2 1 2
s
a powH1 #pow helper
n 1
copy 2 _pow
s
a powH2 #pow helper
powH3 1 2
powH2 1 2
s
a powH3 #pow helper
d _pow
mulBy 1 2
f
#Running Tests:
p A
p B
p C
n A #A = 1
n B #B = 1
add C A B #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B #d = d * B = 6 * 3 = 18
p ____
p d
d A #A = A - 1 = 1 - 1 = 0
mulBy d A #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C
(Wenn Sie mit diesem Code herumspielen, beachten Sie, dass viele der Befehle fehlschlagen, wenn dieselbe Variable mehrmals als Argument angegeben wird. Dies kann leicht behoben werden, der resultierende Code ist jedoch länger.)
Ihr Prindeal-Interpreter sollte in der Lage sein, die genaue Ausgabe zu erstellen:
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
Wertung
Der kürzeste Code in Bytes gewinnt. Tiebreaker geht auf frühere Vorlage.
Brownie Bonus: Schreiben Sie ein cooles Programm in Prindeal. Ich habe Addition und Multiplikation implementiert. Können Sie subtrahieren oder dividieren?
p
, undp p
was würde dann 1 ausgeben, richtig?