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 varund 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 printFunktionstyp 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 printoder "Hey"zu einem Pattern and expression disagreeFehler.
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.toStringFunktion, um das Entziehen von Zeichenfolgen zu handhaben, und hat die allgemeine Form <code>"<data>", wobei "<data>"es sich um eine entkoppelte Zeichenfolge der codeVorgä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 endin den Körper der fnAbstraktion schreiben print(...). Das Entfernen eines Teils der Zeichenfolge führt zu einer Länge von weniger als 189, wodurch eine BindAusnahme ausgelöst wird.
Es gibt noch ein Problem: Der gesamte val 195=size tScheck 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, tund uum 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".