";str(chr 34)^it;(print(it^it);fn x=>print(x^it^x^it))";str(chr 34)^it;(print(it^it);fn x=>print(x^it^x^it))
Nicht zitierte Version: Probieren Sie es auf Codingground.
Zitierte Version: Probieren Sie es auf Codingground.
Beachten Sie, dass die Ausgabe ungefähr so aussieht
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
weil der Code deklarationsweise interpretiert wird (jede Deklaration ;
endet) und den Wert und den Typ jeder Deklaration anzeigt.
Hintergrund
In SML gibt es eine Quine der Form <code>"<code in quotes>"
:
str(chr 34);(fn x=>print(x^it^x^it))"str(chr 34);(fn x=>print(x^it^x^it))"
und eine in der Form "<code in quotes>"<code>
:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
Beide stützen sich auf die Tatsache, dass der <code>
-Teil keine Anführungszeichen enthält und somit ohne die Notwendigkeit, irgendetwas zu entgehen, zitiert werden kann, die "
zur Ausgabe des Quines erforderlich sind, gegeben durch str(chr 34)
.
Sie hängen auch stark von der impliziten Kennung ab, it
die verwendet wird, wenn in einer Deklaration keine explizite Kennung angegeben ist.
Im ersten str(chr 34);
bindet quine an it
die Zeichenkette, die enthält "
, fn x=>
startet eine anonyme Funktion mit einem Argument x
, verkettet x^it^x^it
und druckt die resultierende Zeichenkette. Diese anonyme Funktion wird direkt auf eine Zeichenfolge angewendet, die den Programmcode enthält, sodass sich die Verkettung x^it^x^it
ergibt <code>"<code>"
.
Das zweite Quine beginnt nur mit dem Programmcode als String, an ";str(chr 34)^it;print(it^it)";
den gebunden wird it
. Dann str(chr 34)^it;
verkettet ein Zitat an den Anfang des Strings und als wieder keine explizite Kennung gegeben wird, den resultierenden String"<code>
ist gebunden it
. Schließlich print(it^it)
verkettet die Zeichenfolge mit sich selbst ergibt , "<code>"<code>
die dann gedruckt wird.
Erläuterung
Bearbeiten: Mit der 108-Byte-Version nicht mehr auf dem neuesten Stand, aber man könnte es auch nach dem Lesen dieser Erklärung verstehen.
Die Quine, die für Anführungszeichen sicher ist, kombiniert beide oben genannten Ansätze und ist selbst von der Form "<code>"<code>
. Wenn Sie dies erneut in Anführungszeichen setzen ""<code>"<code>"
, erhalten Sie eine leere Zeichenfolge und dann eine Quine der anderen Form.
Das heißt, das Programm erhält entweder eine eigene Quelle in Form "<code>
des Bezeichners it
, oder es it
ist gerecht "
und wir erhalten eine eigene Quelle<code>
als Argument und müssen daher eine Funktion sein, die mit einem solchen Argument umgeht.
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
Um festzustellen, in welchem Fall wir uns befinden, prüfen wir, ob die Größe von it
größer als 1 ist. Wenn nicht, it
ist dies "
der Fall, und wir befinden uns im zweiten Fall, sodass der else
-Teil eine anonyme Funktion zurückgibtfn x=>print(it^it^x^it^x^it)
die dann aufgerufen wird, weil die Quelle als Zeichenfolge folgt . Beachten Sie den it^it^
Zeilenabstand, der zu Beginn des Programms für die leere Zeichenfolge benötigt wird.
Wenn size it
größer als 1 ist, sind wir im then
-Part und führen einfach durch print(it^it)
, oder? Nicht ganz, weil ich versäumt habe, Ihnen zu sagen, dass SML stark typisiert ist, was bedeutet, dass eine Bedingung if <cond> then <exp_1> else <exp_2>
immer den gleichen Typ haben muss, was wiederum bedeutet, dass die Ausdrücke <exp_1>
und <exp_2>
der gleiche Typ sein müssen. Wir kennen bereits den Typ des else
Teils: Eine anonyme Funktion, die eine Zeichenfolge verwendet und dann aufruft, print
hat den Typ string -> <return type of print>
und print
den Typ string -> unit
( unit
ist in gewisser Weise ähnlich wievoid
in anderen Sprachen), daher lautet der resultierende Typ erneut string -> unit
.
Wenn das then
Teil also nur print(it^it)
den Typ hat unit
, erhalten wir einen Typinkongruenzfehler. Also wie wäre es fn _=>print(it^it)
? ( _
Ist ein Platzhalter für ein Argument , das nicht verwendet wird) Diese anonyme Funktion auf seine eigene Art hat , 'a -> unit
wo 'a
für einen beliebigen Typ steht, so im Rahmen unserer bedingten , die eine erzwingt string -> unit
Art dies funktionieren würde. (Die Variable type 'a
wird mit type instanziiert string
.) In diesem Fall würden wir jedoch nichts ausgeben, da die anonyme Funktion niemals aufgerufen wird! Denken Sie daran, wenn wir in den then
-teil gehen, ist der Gesamtcode "<code>"<code>
, so dass der <code>
-teil zu einer Funktion ausgewertet wird, aber da nichts danach kommt, wird er nicht aufgerufen.
Stattdessen wir eine Sequentialisierung verwenden , die die Form hat , (<exp_1>; ...; <exp_n>)
wo <exp_1>
auf <exp_n-1>
beliebige Arten und die Art kann von <exp_n>
der Art der ganzen Sequentialisierung bietet. Aus funktionaler Sicht werden die Werte von <exp_1>
to <exp_n-1>
einfach verworfen, SML unterstützt jedoch auch imperative Konstrukte, sodass die Ausdrücke möglicherweise Nebenwirkungen haben. Kurz gesagt, wir nehmen (print(it^it);print)
als then
-teil, also zuerst drucken und dann die Funktion zurückgeben, print
die den richtigen Typ hat.