--"#p=p['"']>>"--\""#p
Версия без кавычек: Попробуйте это на codingground.
Цитируемая версия: Попробуйте это на codingground.
Обратите внимание, что результат выглядит примерно так
"--"#p=p['"']>>"--\""#p
k#p=p x>>print x>>p k;main="--"#putStr;x="--\"#p=p['\"']>>\"--\\\"\"#p\nk#p=p x>>print x>>p k;main=\"--\"#putStr;x="
--"
потому что код интерпретируется объявление за объявлением (каждое --"#p=p['"']>>"--\""#p
k#p=p x>>print x>>p k;main="--"#putStr;x="--\"#p=p['\"']>>\"--\\\"\"#p\nk#p=p x>>print x>>p k;main=\"--\"#putStr;x="
--
ends a declaration) and shows the value and type of each declaration.
Фон
В SML есть квайн вида "`...`Dqp^≈[_\"pǏ]₴"
" Push the set [0, 0]
`...` Push the source code
D Make 3 copies of the source code
qp Uneval the first copy and prepend it to the second
^ Reverse the stack, putting [0, 0] on the top
≈[ ] Execute if all elements of the top of the stack are the same.
This is the case because of the [0, 0] pushed by "
_ Remove the third copy of the source from the stack
\" Push `"`
pǏ Prepend and enclose the source with `"`
₴ Print the source
" Push [0, 0], having no effect because the source is already printed
:
`...`Dqp^≈[_\"pǏ]₴
`...` Push half the source code
D Make 3 copies of the source code
qp Uneval the first copy (quote, escape) and prepend it to the second
^ Reverse the stack
≈[_\"pǏ] Execute if all elements are the same.
Since this is not the case, it just pops the third copy of the source code
₴ Print the source
и один в форме `Dqp^≈[_\\"pǏ]₴`Dqp^≈[_\"pǏ]₴
:
D
Оба исходят из того, что x
-part contains no quotes and can thus be quoted with out the need for escaping anything, the "
необходимые для вывода куайна, имеют вид #
.
Они также в значительной степени полагаются на неявный идентификатор. s='s=%r;print(s%%s)';print(s%s)
which is used when no explicit identifier is given in a declaration.
В первом куайне x
binds x='"'
в строку, содержащую "#"
, "x="
запускает анонимную функцию, принимающую один аргумент "x=""#";x='"'
, then concatenates x=""
и печатает полученную строку. Эта анонимная функция применяется непосредственно к строке, содержащей код программы, поэтому конкатенация #
yields x=""#";x='"'
.
Второй куайн начинается с кода программы в виде строки. x=""#";x='"'
s='x=""#";x=\'"\'\ns=%r;print(x+s%%s+x)#';print(x+s%s+x)#
which is bound to x=""#";x='"'
s='x=""#";x=\'"\'\ns=%r;print x+s%%s+x#';print x+s%s+x#
. Затем "
concatenates a quote to the start of the string and as again no explicit identifier is given, the resulting string print
обязан $\
. Finally q;...;
объединяет строку сама с собой, получая q;";
which is then printed.
Объяснение
Редактировать: 108-байтовая версия уже не актуальна, однако это можно понять и после прочтения этого объяснения.
Кавычно-безопасный куайн сочетает в себе оба вышеперечисленных подхода и сам имеет форму $\
. Putting this in again in quotes yields ;
, поэтому мы получаем пустую строку, а затем квину другой формы.
Это означает, что программе либо дается собственный исходный код в виде ""
by the identifier #
, или $\
is just print
и нам дан наш собственный источник ;;
as argument and must thus be a function which handles such an argument.
";$\=q;"
Чтобы определить, в каком случае мы находимся, мы проверяем, соответствует ли размер "";$\=q;";;$_=q(print$\,'";$\=q;";;$_=q('."$_);eval#");eval#"
is larger than 1. If not then ";$\=q;";;$_=q(print$\,'";$\=q;";;$_=q('."$_);eval#");eval#
является Set=``;eval(";a='Set=``;eval(~;a=1;S=String.fromCharCode;q=S(34);r=Set&&q;s=S(39);alert(r+a.replace(/[^ -}]/g,q).replace(1,s+a+s)+r);~)';S=String.fromCharCode;q=S(34);r=Set&&q;s=S(39);alert(r+a.replace(/[^ -}]/g,q).replace(1,s+a+s)+r);")
and we are in the second case, so the <div id="noodel" code='"ḷḥ-Ðḥ@ḅ"' input="" cols="10" rows="1"></div>
<script src="https://tkellehe.github.io/noodel/noodel-latest.js"></script>
<script src="https://tkellehe.github.io/noodel/ppcg.min.js"></script>
-part возвращает анонимную функцию <div id="noodel" code='ḷḥ-Ðḥ@ḅ' input="" cols="10" rows="1"></div>
<script src="https://tkellehe.github.io/noodel/noodel-latest.js"></script>
<script src="https://tkellehe.github.io/noodel/ppcg.min.js"></script>
which is then called because its followed by source as string. Note the leading "ḷḥ-Ðḥ@ḅ"
" # Pushes on the string literal "\"" onto the stack.
ḷḥ-Ðḥ@ḅ # Same execution as before, simply builds the Quine for this loop.
ḷ # Loop the following block of code unconditionally.
ḥ- # Push the string literal of the token preceding this one which pushes "ḷ" onto the stack.
Ð # Push the stack as an array to stdout (since is an array it is done by reference).
ḥ@ # Push the string literal for this block of code which pushes "ḥ-Ðḥ@ḅ" onto the stack.
ḅ # Break out of the given loop. (The stack is now ["\"", "ḷ", "ḥ-Ðḥ@ḅ"]).
" # Pushes on the string literal "\"" onto the stack.
# The top of the stack is popped off and displayed which modifies the array to produce {["\"", "ḷ", "ḥ-Ðḥ@ḅ"], "\""} in stdout.
который необходим для пустой строки в начале программы.
Если "
is larger than 1 we are in the ḷḥ-Ðḥ@ḅ # Single statement that builds itself as a string.
ḷ # Loop the following block of code unconditionally.
ḥ- # Push the string literal of the token preceding this one which pushes "ḷ" onto the stack.
Ð # Push the stack as an array to stdout (since is an array it is done by reference).
ḥ@ # Push the string literal for this block of code which pushes "ḥ-Ðḥ@ḅ" onto the stack.
ḅ # Break out of the given loop. (The stack is now ["ḷ", "ḥ-Ðḥ@ḅ"]).
# The top of the stack is popped off and displayed which modifies the array to produce {["ḷ"], "ḥ-Ðḥ@ḅ"} in stdout.
-расставаться и просто выступать ḷḥ-Ðḥ@ḅ
, right? Not quite, because I neglected to tell you that SML is strongly typed which means that a conditional Ḷ1ḥ-Ð1ḥ@€
всегда должны иметь один и тот же тип, что снова означает, что выражения l " Move one character to the right. If there is no character to the right,
" then this is effectively a "break" statement, stopping playback of the recording
xx " Delete two characters (the '"P')
| " Move to the first character on this line
x " Delete one character
ÿ " End the program
и "qp
необходимо иметь один и тот же тип. Мы уже знаем тип "éPñi"éP<C-v>241<esc>"qpá"
Cursor is here ----------^
part: An anonymous function which takes a string and then calls "éPñi"éP<C-v>241<esc>"qpá"P
Cursor is here ----------^
имеет тип ñ " Start recording into register 'q'
i " Enter insert mode
"éP<C-v>241<esc> " Enter the following text: '"éPñ'
"qp " Paste the text in register 'q'
á" " Append a '"'
, and ñi<C-v>241<esc>"qpÿ
имеет тип "éP
( éP
в чем-то похоже на 00000000: e950 f169 22e9 5016 3234 311b 2271 70e1 .P.i".P.241."qp.
00000010: 226c 7878 7c78 ff "lxx|x.
in other languages), so the resulting type is again éPñi"éP<C-v>241<esc>"qpá"lxx|xÿ
.
Итак, если éPñi"éP241"qpá"lxx|xÿ
part was just print
который имеет тип then
, we would get a type mismatch error. So how about (print(it^it);print)
? ( <exp_n-1>
is a wildcard for an argument that is not used) This anonymous function on its own has type <exp_1>
где <exp_n>
stands for an arbitrary type, so in the context of our conditional which enforces a <exp_n-1>
типа это сработает. (Переменная типа <exp_1>
gets instantiated with type (<exp_1>; ...; <exp_n>)
.) Однако в этом случае мы бы ничего не печатали, поскольку анонимная функция никогда не вызывается! Помните, когда мы заходим в <code>
-part the overall code is "<code>"<code>
, поэтому then
-part evaluates to a function but, as nothing comes after it, its not called.
Вместо этого мы используем секвенциализацию, которая имеет форму string
where 'a
to string -> unit
may have arbitrary types and the type of 'a
обеспечивает тип всей секвенциализации. С функциональной точки зрения значения 'a -> unit
to _
просто отбрасываются, однако SML также поддерживает императивные конструкции, поэтому выражения могут иметь побочные эффекты. Короче говоря, мы берем fn _=>print(it^it)
as the unit
-part, таким образом сначала печатая, а затем возвращая функцию print(it^it)
which has the correct type.