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 Funktionfund einen Wertxaus dem Stapel ein und giltffürx. WennfArity 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-Funktiongauf den Stack verschoben . Es nimmt Eingaben und kehrt zurück .x1,x2,...,xn-1f(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 Wertxwird zugeordnet[x,x].>( shift ) schiebt eine unäre Funktion, die einen-ary-Funktion übernimmt, auf den Stapelfund gibt eine(n+1)-ary-Funktion zurückg, die ihr erstes Argument ignoriertx,fdie verbleibenden aufruft undxvor dem Ergebnis angreift . Zum Beispielshift(clone)ist eine Binärfunktion, die Eingabena,bund 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 siealeer ist, und[c]ansonsten.$( Anruf ) drückt auf den Stapel eine binäre Funktion , die eine Funktion öffnetfund einen Wertx, und wendetfaufxgenau wie der!Fall ist..( chain ) schiebt eine Binärfunktion auf den Stapel, die zwei Funktionenfundgund ihre Zusammensetzung zurückgibt: Eine Funktionh, die dieselbe Arität wiefhat und deren Eingaben normal sind, giltffür sie und dann vollständig fürgdas Ergebnis (Aufrufe) es so oft, wie es seine Arität vorschreibt), mit unbenutzten Gegenständen aus der Ausgabe desfVerbleibs im Ergebnis vonh. Zum Beispiel sei angenommen , daßfeine binäre Funktion ist, die ihr zweites Argument Klonen undgist 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 ,fwird es zuerst angewendeta,b, um zu produzieren[a,b,b]wird danngauf 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,0wenn es ein Leerzeichen war und1wenn 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, 0s oder 1s 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 0s und 1s 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 nZeichen 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 bkopiert, dann das erste kopiert aund mit sich selbst komponiert. Anschließend wendet Sie die Komposition auf die Kopie von an bund 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,xals 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 1s 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 nsolchen Anwendungen hatte die verbleibende Funktion nur ein Argument und berechnet zu diesem Zeitpunkt f(z1, z2, ..., zn)(dh fangewendet auf alle Argumente, die Sie eingegeben haben), wodurch einige neue Werte übertragen werden, und verwendet dann sofort mWerte aus dem Stapel und ruft gsie auf.
.funktioniert genau wie von Martin beschrieben, außer dass das Ergebnis undefiniert ist , wenn feine Liste mit weniger als mWerten zurückgegeben wird (die Komposition hat Arität n, sodass sie nicht mehr Argumente vom Stapel essen kann). Im Wesentlichen wird die Ausgabe von fals temporärer Stapel verwendet, auf den mal mit ggeschoben und angewendet mwird !, und das Ergebnis davon wird dem Hauptstapel hinzugefügt.