";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 itque é 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^ite 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^itgera<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 itou 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 elseparte-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 itfor maior que 1, estamos na thenparte 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 elsepeça: uma função anônima que pega uma string e depois chama printpossui type string -> <return type of print>, e printtype string -> unit( unité de alguma forma semelhante a voidoutros idiomas), portanto o tipo resultante é novamentestring -> unit .
Portanto, se a thenpeç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 -> unitonde 'arepresenta um tipo arbitrário; portanto, no contexto de nossa condicional que aplica um string -> unittipo, 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 thenparte -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)thenprint