val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]
Probieren Sie es online!
Für MLton sind vollständige SML-Programme entweder Ausdrücke, die durch ;
(z. B. print"Hello";print"World";
) begrenzt und abgeschlossen werden, oder Deklarationen mit den Schlüsselwörtern var
und fun
(z. B. var _=print"Hello"var _=print"World"
), wobei _
es sich um einen Platzhalter handelt, der auch durch einen beliebigen Variablennamen ersetzt werden kann.
Die erste Option ist für makellose Programmierung nutzlos, da es sich ;
für sich genommen um ein gültiges Programm handelt (das nichts tut, aber auch keinen Fehler macht). Das Problem mit dem zweiten Ansatz ist, dass Deklarationen wie var _=print"Hello"
nur var _="Hello"
(oder sogar gekürzt werden könnenvar _=print
) weil die Deklaration mitvar
funktioniert, solange die rechte Seite ein gültiger SML-Ausdruck oder -Wert ist (SML ist eine funktionale Sprache, also können Funktionen sein) auch als Werte verwendet).
Zu diesem Zeitpunkt war ich bereit, makellose Programmierung in SML für unmöglich zu erklären, als ich zufällig auf Pattern Matching in val
-Deklarationen stieß. Es stellt sich heraus, dass die Syntax für Deklarationen nicht val <variable_name> = <expression>
aber so ist val <pattern> = <expression>
, dass ein Muster aus Variablennamen, Konstanten und Konstruktoren bestehen kann. Da der print
Funktionstyp hat string -> unit
, können wir eine Mustererkennung auf der Verwendung unit
-Wertes ()
zu erzwingen , dass die Druckfunktion tatsächlich auf den String angewandt wird: val()=print"Hey"
. Bei diesem Ansatz führt das Entfernen von entweder print
oder "Hey"
zu einem Pattern and expression disagree
Fehler.
Mit dieser Art des makellosen Druckens besteht der nächste Schritt darin, eine Quine zu schreiben, bevor schließlich weitere Sicherheitsmaßnahmen hinzugefügt werden müssen. Früher habe ich bisher eine einfache SML quine Technik (die sehen Revisionsgeschichte ), aber Anders Kaseorg wies einen anderen Ansatz aus , der einige Bytes in seinem Fall speichern. Es verwendet die eingebaute String.toString
Funktion, um das Entziehen von Zeichenfolgen zu handhaben, und hat die allgemeine Form <code>"<data>"
, wobei "<data>"
es sich um eine entkoppelte Zeichenfolge der code
Vorgängerversion handelt:
val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"
Dies ist eine funktionierende Quine, aber noch nicht makellos. Zunächst stellte Anders Kaseorg fest, dass MLton ein einfaches Anführungszeichen "
als Code akzeptiert, ohne Fehler zu erzeugen. Dies bedeutet, dass der Code nicht wie oben in einem Anführungszeichen endet. Der kürzeste Weg, dies zu verhindern, wäre, alles nachher val()=
in Klammern zu setzen, aber dann könnte der Code auf reduziert werden val()=()
. Der zweitkürzeste Weg, den ich gefunden habe, besteht darin val()=hd[ ... ]
, alles in eine Liste zu packen und das erste Element zurückzugeben, um den Typprüfer glücklich zu machen.
Damit kein Teil des Datenstrings unbemerkt entfernt werden kann, bietet sich die Mustererkennung in val
-declarations wieder an: Die Länge des endgültig zu druckenden Strings (und damit die Programmlänge) sollte also 195 betragen wir können stattdessen let val t=... val 195=size t in print t end
in den Körper der fn
Abstraktion schreiben print(...)
. Das Entfernen eines Teils der Zeichenfolge führt zu einer Länge von weniger als 189, wodurch eine Bind
Ausnahme ausgelöst wird.
Es gibt noch ein Problem: Der gesamte val 195=size t
Scheck könnte einfach fallengelassen werden. Wir können dies verhindern, indem wir das Häkchen so erweitern, dass es mit einem Tupel übereinstimmt : val t=... val(216,u)=(n+size t,t)in print u end
, sodass das Entfernen des Häkchens zu einer ungebundenen Variablen führt u
.
Insgesamt ergibt dies die folgende 195-Byte-Lösung:
val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]
Die Anwendung des Golf - Tricks mit Operator Variablennamen wie !
, $
und %
statt n
, t
und u
um etwas Platz zu sparen (siehe diesen Tipp ) führt zur endgültigen 182 Byte - Version.
Alle anderen Teilzeichenfolgenentfernungen, die in der Erläuterung nicht explizit angegeben wurden, sollten zu einem Syntax- oder Typfehler führen.
Edit 1: length(explode t)
ist gerade size t
.
Edit 2: Vielen Dank an Anders Kaseorg für einen anderen Ansatz und den Hinweis auf eine "Sicherheitslücke".