";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))
Versão sem aspas : Experimente no codingground.
Versão citada : Experimente no codingground.
Observe que a saída se parece com isso
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
porque o código é interpretado declaração por declaração (cada ;
uma termina uma declaração) e mostra o valor e o tipo de cada declaração.
fundo
No SML, existe um quine do formulário <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 um na forma "<code in quotes>"<code>
:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
Ambos confiam no fato de que a <code>
parte-não contém aspas e, portanto, pode ser citada sem a necessidade de escapar de nada, o "
necessário para produzir a coluna é dado porstr(chr 34)
.
Eles também dependem muito do identificador implícito it
que é usado quando nenhum identificador explícito é fornecido em uma declaração.
Na primeira linha, str(chr 34);
liga it
- se à cadeia que contém"
, fn x=>
inicia uma função anônima usando um argumento x
, concatena x^it^x^it
e imprime a string resultante. Essa função anônima é aplicada diretamente a uma sequência que contém o código do programa, portanto a concatenação x^it^x^it
gera<code>"<code>"
.
O segundo quine começa com apenas o código do programa como string ao ";str(chr 34)^it;print(it^it)";
qual está vinculado it
. Entãostr(chr 34)^it;
concatena uma cotação para o início da sequência e, como nenhum identificador explícito é fornecido, a sequência resultante "<code>
é vinculada it
. Por fim, print(it^it)
concatena a string com ela mesma, produzindo a "<code>"<code>
qual é impressa.
Explicação
Editar: não está mais atualizado com a versão de 108 bytes, no entanto, pode-se entender também depois de ler esta explicação.
O quine seguro para cotação combina as duas abordagens acima e é ele próprio da forma "<code>"<code>
. Colocando isso novamente entre aspas ""<code>"<code>"
, obtemos uma string vazia e, em seguida, uma coluna da outra forma.
Isso significa que o programa recebe sua própria fonte no formulário "<code>
pelo identificador it
ou it
é justo "
e recebemos nossa própria fonte<code>
como argumento e, portanto, deve ser uma função que lida com esse argumento.
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
Para identificar em qual caso estamos, verificamos se o tamanho de it
é maior que 1. Se não, então it
é "
e estamos no segundo caso, portanto, a else
parte-retorna uma função anônimafn x=>print(it^it^x^it^x^it)
que é chamada então porque é seguida pela origem como string . Observe a liderança it^it^
necessária para a sequência vazia no início do programa.
Se size it
for maior que 1, estamos na then
parte e apenas executamos print(it^it)
, certo? Não exatamente porque deixei de lhe dizer que SML é fortemente tipado, o que significa que uma condicional if <cond> then <exp_1> else <exp_2>
deve sempre ter o mesmo tipo, o que novamente significa que as expressões <exp_1>
e <exp_2>
precisam ter o mesmo tipo. Já sabemos o tipo da else
peça: uma função anônima que pega uma string e depois chama print
possui type string -> <return type of print>
, e print
type string -> unit
( unit
é de alguma forma semelhante a void
outros idiomas), portanto o tipo resultante é novamentestring -> unit
.
Portanto, se a then
peça tivesse apenas o print(it^it)
tipo unit
, receberíamos um erro de incompatibilidade de tipo. Então, que tal fn _=>print(it^it)
? ( _
é um curinga para um argumento que não é usado) Essa função anônima, por si só, possui um tipo 'a -> unit
onde 'a
representa um tipo arbitrário; portanto, no contexto de nossa condicional que aplica um string -> unit
tipo, isso funcionaria. (A variável type 'a
é instanciada com type string
.) No entanto, nesse caso, não imprimiríamos nada, pois a função anônima nunca é chamada! Lembre-se, quando entramos na then
parte -part, o código geral é "<code>"<code>
, então a <code>
parte-avalia como uma função, mas, como nada vem depois dela, ela não é chamada.
Em vez disso, usamos uma sequencialização que tem a forma para (<exp_1>; ...; <exp_n>)
onde pode ter tipos arbitrários e o tipo de fornece o tipo de toda a sequencialização. Do ponto de vista funcional, os valores de to são simplesmente descartados, no entanto, o SML também suporta construções imperativas, para que as expressões possam ter efeitos colaterais. Em resumo, tomamos como parte, imprimindo primeiro e retornando a função que tem o tipo correto.<exp_1>
<exp_n-1>
<exp_n>
<exp_1>
<exp_n-1>
(print(it^it);print)
then
print