";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))
Versione non quotata : provalo su codingground.
Versione quotata: provalo su codingground.
Si noti che l'output è simile a questo
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
perché il codice viene interpretato dichiarazione per dichiarazione (ognuno ;
termina una dichiarazione) e mostra il valore e il tipo di ogni dichiarazione.
sfondo
In SML c'è un quine del modulo <code>"<code in quotes>"
:
str(chr 34);(fn x=>print(x^it^x^it))"str(chr 34);(fn x=>print(x^it^x^it))"
e uno nella forma "<code in quotes>"<code>
:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
Entrambi si basano sul fatto che la <code>
parte non contiene virgolette e può quindi essere quotata senza la necessità di sfuggire a qualsiasi cosa, i "
necessari per produrre il quine sono dati dastr(chr 34)
.
Si basano fortemente anche sull'identificatore implicito it
che viene utilizzato quando non viene fornito un identificatore esplicito in una dichiarazione.
Nel primo quine si str(chr 34);
lega it
alla stringa contenente "
, fn x=>
avvia una funzione anonima prendendo un argomento x
, quindi concatena x^it^x^it
e stampa la stringa risultante. Questa funzione anonima viene applicata direttamente a una stringa contenente il codice del programma, quindi la concatenazione x^it^x^it
produce <code>"<code>"
.
La seconda riga inizia con solo il codice del programma come stringa a ";str(chr 34)^it;print(it^it)";
cui è associato it
. Quindi str(chr 34)^it;
concatena una citazione all'inizio della stringa e poiché non viene fornito alcun identificatore esplicito, la stringa risultante "<code>
viene associata it
. Infine print(it^it)
concatena la stringa con se stessa cedendo "<code>"<code>
che viene quindi stampata.
Spiegazione
Modifica: non più aggiornato con la versione da 108 byte, tuttavia è possibile capirlo anche dopo aver letto questa spiegazione.
Il quine sicuro di virgolette combina entrambi gli approcci di cui sopra ed è esso stesso della forma "<code>"<code>
. Mettendolo di nuovo tra virgolette ""<code>"<code>"
, otteniamo una stringa vuota e poi una quinta dell'altra forma.
Ciò significa che al programma viene data la propria fonte nella forma "<code>
dall'identificatore it
, oppure it
è giusta "
e ci viene data la nostra fonte <code>
come argomento e deve quindi essere una funzione che gestisce tale argomento.
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
Per identificare in quale caso siamo, controlliamo se la dimensione di it
è maggiore di 1. Altrimenti lo it
è "
e siamo nel secondo caso, quindi la else
parte restituisce una funzione anonima fn x=>print(it^it^x^it^x^it)
che viene quindi chiamata perché è seguita da source come stringa . Nota il carattere iniziale it^it^
necessario per la stringa vuota all'inizio del programma.
Se size it
è maggiore di 1 siamo nella then
parte e ci esibiamo print(it^it)
, giusto? Non del tutto, perché ho trascurato di dirti che SML è fortemente tipizzato, il che significa che un condizionale if <cond> then <exp_1> else <exp_2>
deve sempre avere lo stesso tipo, il che significa che le espressioni <exp_1>
e <exp_2>
devono avere lo stesso tipo. Conosciamo già il tipo di else
parte: una funzione anonima che prende una stringa e quindi chiama print
ha tipo string -> <return type of print>
e print
ha tipo string -> unit
( unit
è in qualche modo simile ad void
altre lingue), quindi il tipo risultante è di nuovostring -> unit
.
Quindi, se la then
parte fosse proprio di print(it^it)
tipo unit
, avremmo un errore di mancata corrispondenza del tipo. E allora fn _=>print(it^it)
? ( _
è un jolly per un argomento che non viene utilizzato) Questa funzione anonima di per sé ha un tipo 'a -> unit
dove 'a
sta per un tipo arbitrario, quindi nel contesto del nostro condizionale che impone un string -> unit
tipo che funzionerebbe. (La variabile type 'a
viene istanziata con type string
.) Tuttavia, in questo caso non stamperemmo nulla poiché la funzione anonima non viene mai chiamata! Ricorda, quando andiamo nella then
parte, il codice generale lo è "<code>"<code>
, quindi la parte <code>
restituisce una funzione ma, poiché nulla viene dopo di essa, non viene chiamata.
Invece usiamo un sequentialisation che ha la forma (<exp_1>; ...; <exp_n>)
dove <exp_1>
per <exp_n-1>
può avere tipi arbitrari e il tipo di <exp_n>
fornisce il tipo di tutta sequentialisation. Da un punto di vista funzionale i valori di <exp_1>
to <exp_n-1>
vengono semplicemente scartati, tuttavia SML supporta anche costrutti imperativi, quindi le espressioni possono avere effetti collaterali. In breve, prendiamo (print(it^it);print)
come then
-part, quindi prima stampando e poi restituendo la funzione print
che ha il tipo corretto.