StandardML , 182 176 108 bajtów
";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))
Wersja bez cudzysłowu : Wypróbuj na polu kodowania.
Cytowana wersja: Wypróbuj na polu kodowania.
Zauważ, że dane wyjściowe wyglądają mniej więcej tak
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
ponieważ kod jest interpretowany deklaracja po deklaracji (każda ;kończy deklarację) i pokazuje wartość i typ każdej deklaracji.
tło
W SML znajduje się quine formy <code>"<code in quotes>":
str(chr 34);(fn x=>print(x^it^x^it))"str(chr 34);(fn x=>print(x^it^x^it))"
i jeden w formie "<code in quotes>"<code>:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
Oba opierają się na fakcie, że <code>-part nie zawiera cudzysłowów i dlatego mogą być cytowane bez potrzeby uciekania przed czymkolwiek, "potrzebne do wyprowadzenia quine są podane przezstr(chr 34) .
Opierają się również w dużej mierze na niejawnym identyfikatorze it który jest używany, gdy w deklaracji nie podano wyraźnego identyfikatora.
W pierwszej quine str(chr 34);wiąże itłańcuch zawierający ", fn x=>uruchamia anonimową funkcję, biorąc jeden argument x, a następnie konkatenuje x^it^x^iti wypisuje powstały ciąg. Ta anonimowa funkcja jest bezpośrednio stosowana do ciągu zawierającego kod programu, więc konkatenacja się x^it^x^itkończy <code>"<code>".
Druga quine zaczyna się od samego kodu programu jako łańcucha, z ";str(chr 34)^it;print(it^it)";którym jest związany it. Następnie str(chr 34)^it;łączy cytat z początkiem łańcucha i ponieważ ponownie nie podano wyraźnego identyfikatora, wynikowy łańcuch "<code>jest związany it. Na koniec print(it^it)konkatenuje ciąg z samym sobą, "<code>"<code>co jest następnie drukowane.
Wyjaśnienie
Edytować: Nie jest już na bieżąco z wersją 108-bajtową, jednak można ją również zrozumieć po przeczytaniu tego wyjaśnienia.
Quine-bezpieczny quine łączy oba powyższe podejścia i sam jest w formie "<code>"<code> . Ponowne umieszczenie tego w kwotowaniach daje""<code>"<code>" , więc otrzymujemy pusty ciąg, a następnie quine innej formy.
Oznacza to, że program albo otrzymuje własne źródło w formie "<code>identyfikatora it, albo itjest sprawiedliwy "i podajemy własne źródło <code>jako argument, a zatem musi to być funkcja, która obsługuje taki argument.
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
Aby określić, w którym przypadku jesteśmy, sprawdzamy, czy rozmiar itjest większy niż 1. Jeśli nie, to itjest "i jesteśmy w drugim przypadku, więc else-part zwraca anonimową funkcję, fn x=>print(it^it^x^it^x^it)która jest następnie wywoływana, ponieważ następuje po niej źródło jako ciąg . Zwróć uwagę na wiodące, it^it^które jest potrzebne dla pustego ciągu na początku programu.
Jeśli size itjest większy niż 1, jesteśmy w części theni po prostu wykonujemy print(it^it), prawda? Niezupełnie, ponieważ zlekceważyłem powiedzieć, że SML jest mocno wpisany, co oznacza, że warunek if <cond> then <exp_1> else <exp_2>musi zawsze mieć ten sam typ, co ponownie oznacza, że wyrażenia <exp_1>i <exp_2>muszą mieć ten sam typ. Znamy już typ elseczęści: anonimowa funkcja, która pobiera ciąg znaków, a następnie wywołuje, printma typ string -> <return type of print>i printma typ string -> unit( unitjest w pewien sposób podobny do voidinnych języków), więc typ wynikowy jest ponownie string -> unit.
Więc jeśli thenczęść była właśnie print(it^it)tego typu unit, otrzymalibyśmy błąd niedopasowania typu. Co powiesz na fn _=>print(it^it)? ( _jest symbolem wieloznacznym dla argumentu, który nie jest używany) Ta anonimowa funkcja sama w sobie ma typ, 'a -> unitgdzie 'aoznacza dowolny typ, więc w kontekście naszego warunku, który wymusza string -> unittyp, zadziałałoby. (Zmienna typu 'ajest tworzona z typem string.) Jednak w tym przypadku nie wydrukowalibyśmy niczego, ponieważ funkcja anonimowa nigdy nie jest wywoływana! Pamiętaj, że kiedy wchodzimy w then-part, ogólny kod jest "<code>"<code>taki, że<code> -part ocenia funkcję, ale ponieważ nic po nim nie następuje, nie jest wywoływana.
Zamiast tego używamy sequentialisation który ma postać (<exp_1>; ...; <exp_n>)gdzie <exp_1>się <exp_n-1>może mieć dowolne typy i rodzaj <exp_n>zapewnia typ całej sequentialisation. Z funkcjonalnego punktu widzenia wartości <exp_1>to <exp_n-1>są po prostu odrzucane, jednak SML obsługuje również konstrukty imperatywne, więc wyrażenia mogą mieć skutki uboczne. W skrócie, bierzemy (print(it^it);print)jako then-part, drukując najpierw, a następnie zwracając funkcję, printktóra ma poprawny typ.