EDIT: Wie einige von Ihnen vermuteten, gab es einen Fehler im offiziellen Dolmetscher: Die Reihenfolge der Komposition .
wurde umgekehrt. Ich hatte zwei Versionen des Dolmetschers und habe hier die falsche verwendet. Die Beispiele wurden auch für diese falsche Version geschrieben. Ich habe den Interpreter im Repository und die folgenden Beispiele behoben. Die Beschreibung von >
war auch etwas mehrdeutig, also habe ich das behoben. Ich entschuldige mich auch dafür, dass ich so lange gebraucht habe, dass ich in echte Dinge verwickelt war.
EDIT2: Mein Interpreter hatte einen Fehler, dessen Implementierung .
sich in den Beispielen widerspiegelte (sie stützten sich auf undefiniertes Verhalten). Das Problem ist jetzt behoben.
Einführung
Shift ist eine esoterische funktionale Programmiersprache, die ich vor ein paar Jahren erstellt, aber heute veröffentlicht habe. Es ist stapelbasiert, hat aber auch automatisches Currying wie Haskell.
Spezifikation
In Shift gibt es zwei Datentypen:
- Funktionen, die eine beliebige positive Arität (Anzahl der Eingänge) haben und die eine Liste der Ausgänge zurückgeben. Beispielsweise hat eine Funktion, die ihre einzige Eingabe dupliziert, Arität 1, und eine Funktion, die ihre beiden Eingaben vertauscht, hat Arität 2.
- Leerzeichen, die alle identisch sind und keinen anderen Zweck haben, als keine Funktionen zu sein.
Ein Shift-Programm besteht aus null oder mehr Befehlen , von denen jeder ein einzelnes ASCII-Zeichen ist. Insgesamt gibt es 8 Befehle:
!
( apply ) fügt eine Funktionf
und einen Wertx
aus dem Stapel ein und giltf
fürx
. Wennf
Arity 1 vorhanden ist, wird die Listef(x)
an der Vorderseite des Stapels hinzugefügt. Wenn es arity hatn > 1
, wird eine neue(n-1)
-ary-Funktiong
auf den Stack verschoben . Es nimmt Eingaben und kehrt zurück .x1,x2,...,xn-1
f(x,x1,x2,...,xn-1)
?
( leer ) schiebt ein Leerzeichen auf den Stapel.+
( Klon ) schiebt eine unäre Funktion auf den Stapel, die ihre Eingabe dupliziert: Jeder Wertx
wird zugeordnet[x,x]
.>
( shift ) schiebt eine unäre Funktion, die einen
-ary-Funktion übernimmt, auf den Stapelf
und gibt eine(n+1)
-ary-Funktion zurückg
, die ihr erstes Argument ignoriertx
,f
die verbleibenden aufruft undx
vor dem Ergebnis angreift . Zum Beispielshift(clone)
ist eine Binärfunktion, die Eingabena,b
und Rückgaben entgegennimmt[a,b,b]
./
( fork ) schiebt eine ternäre Funktion, die drei Eingaben benötigta,b,c
, auf den Stapel und gibt zurück,[b]
wenn siea
leer ist, und[c]
ansonsten.$
( Anruf ) drückt auf den Stapel eine binäre Funktion , die eine Funktion öffnetf
und einen Wertx
, und wendetf
aufx
genau wie der!
Fall ist..
( chain ) schiebt eine Binärfunktion auf den Stapel, die zwei Funktionenf
undg
und ihre Zusammensetzung zurückgibt: Eine Funktionh
, die dieselbe Arität wief
hat und deren Eingaben normal sind, giltf
für sie und dann vollständig fürg
das Ergebnis (Aufrufe) es so oft, wie es seine Arität vorschreibt), mit unbenutzten Gegenständen aus der Ausgabe desf
Verbleibs im Ergebnis vonh
. Zum Beispiel sei angenommen , daßf
eine binäre Funktion ist, die ihr zweites Argument Klonen undg
ist Anruf . Wenn der Stapel enthält[f,g,a,b,c]
und wir tun.!!
, dann enthält er[chain(f,g),a,b,c]
; Wenn wir es als!!
nächstes tun ,f
wird es zuerst angewendeta,b
, um zu produzieren[a,b,b]
wird danng
auf die ersten beiden Elemente davon angewendet, da seine Arität 2 ist und erzeugt[a(b),b]
, und der Stapel wird schließlich sein[a(b),b,c]
.@
( sagen wir ) drückt eine unäre Funktion, die einfach ihre Eingabe zurückgibt und druckt,0
wenn es ein Leerzeichen war und1
wenn es eine Funktion war.
Beachten Sie, dass alle Befehle außer dem !
einfachen Verschieben eines Werts auf den Stapel keine Möglichkeit zur Eingabe bieten und die einzige Möglichkeit zur Ausgabe die Verwendung ist @
. Ein Programm wird interpretiert, indem die Befehle einzeln ausgewertet, 0
s oder 1
s gedruckt werden, wenn "say" aufgerufen wird, und beendet werden. Jedes hier nicht beschriebene Verhalten (Anwenden eines Leerzeichens, Anwenden eines Stapels der Länge 0 oder 1, Aufrufen von "Kette" auf ein Leerzeichen usw.) ist undefiniert: Der Interpreter kann abstürzen, stillschweigend fehlschlagen, nach Eingaben fragen oder was auch immer.
Die Aufgabe
Ihre Aufgabe ist es, einen Dolmetscher für Shift zu schreiben. Es sollte von STDIN, der Befehlszeile oder dem Funktionsargument ein zu interpretierendes Shift-Programm nehmen und in STDOUT drucken oder die resultierende (möglicherweise unendliche) Ausgabe von 0
s und 1
s zurückgeben. Wenn Sie eine Funktion schreiben, müssen Sie in irgendeiner Weise auf die Ausgaben mit unendlicher Länge zugreifen können (Generator in Python, Lazy List in Haskell usw.). Alternativ können Sie eine andere Eingabe, eine Zahl n
, verwenden und mindestens n
Zeichen der Ausgabe zurückgeben, wenn diese länger als ist n
.
Die niedrigste Byteanzahl gewinnt und Standardschlupflöcher sind nicht zulässig.
Testfälle
Dieses Schichtprogramm druckt 01
:
?@!@@!
Von links beginnend: Drücken Sie ein Leerzeichen, drücken Sie say und wenden Sie das say auf das Leerzeichen an. Dies gibt aus 0
. Drücken Sie dann zweimal auf " Sagen" und wenden Sie das zweite Wort auf das erste an. Dies gibt aus 1
.
Dieses Programm wiederholt sich für immer und erzeugt keine Ausgabe:
$+.!!+!!
Drücken Sie call und clone und wenden Sie dann chain auf sie an (wir benötigen zwei !
s, da chain eine binäre Funktion ist). Jetzt enthält der Stapel eine Funktion, die ein Argument akzeptiert, es dupliziert und die erste Kopie der zweiten aufruft. Mit +!!
duplizieren wir diese Funktion und rufen sie selbst auf.
Dieses Programm druckt 0010
:
?@$.++>!.!!.!!.!!!!+?/!!!@!@>!!!
Schieben Sie ein Leerzeichen und sagen Sie . Erstellen Sie dann eine Binärfunktion, die das zweite Argument b
kopiert, dann das erste kopiert a
und mit sich selbst komponiert. Anschließend wendet Sie die Komposition auf die Kopie von an b
und gibt sie zurück [a(a(b)),b]
. Wenden Sie es auf say und blank an und wenden Sie dann say auf die beiden auf dem Stapel verbleibenden Elemente an.
Dieses Programm druckt 0
. Für jedes !!!
angehängte Element wird ein zusätzliches gedruckt 0
.
?@+$>!>!+>!///!!>!>!.!!.!!.!!+!!!!
Schieben Sie ein Leerzeichen und sagen Sie . Erstellen Sie dann eine ternäre Funktion, die f,g,x
als Eingabe und Rückgabe verwendet wird [f,f,g,g(x)]
. Clone , dass Funktion, und es auf sich, sagen wir , und der Rohling. Diese Anwendung ändert den Stapel nicht, sodass wir die Funktion beliebig oft erneut anwenden können.
Dieses Programm druckt die unendliche Folge 001011011101111...
, wobei die Anzahl der 1
s immer um eins zunimmt:
@?/!@>!??/!!>!+.!!.!!.!!.+>!.!!$$$$+$>!>!$>!>!+>!$>!>!>!+>!>!///!!>!>!>!.!!.!!.!!.!!.!!.!!.!!.!!.!!.!!+!!!!!
Das Repository enthält eine mit Anmerkungen versehene Version.
f(x1, x2, ..., xn)
und g(y1, y2, ..., ym)
. Durch das Aufrufen werden .
beide Popups angezeigt und eine Funktion gedrückt h(z1, z2, ..., zn)
. Jetzt können Sie all diese Argumente auffressen, indem Sie sie schrittweise verarbeiten !
. Nach n
solchen Anwendungen hatte die verbleibende Funktion nur ein Argument und berechnet zu diesem Zeitpunkt f(z1, z2, ..., zn)
(dh f
angewendet auf alle Argumente, die Sie eingegeben haben), wodurch einige neue Werte übertragen werden, und verwendet dann sofort m
Werte aus dem Stapel und ruft g
sie auf.
.
funktioniert genau wie von Martin beschrieben, außer dass das Ergebnis undefiniert ist , wenn f
eine Liste mit weniger als m
Werten zurückgegeben wird (die Komposition hat Arität n
, sodass sie nicht mehr Argumente vom Stapel essen kann). Im Wesentlichen wird die Ausgabe von f
als temporärer Stapel verwendet, auf den mal mit g
geschoben und angewendet m
wird !
, und das Ergebnis davon wird dem Hauptstapel hinzugefügt.