";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))
Версия без кавычек: попробуйте это на codingground.
Цитируемая версия: попробуйте на codingground.
Обратите внимание, что вывод выглядит примерно так
> val it = "{some string}" : string
> val it = "{some string}" : string
{output to stdout}> val it = fn : string -> unit
потому что код интерпретируется декларацией по декларации (каждая ;
заканчивается декларацией) и показывает значение и тип каждой декларации.
Фон
В SML есть формула вида <code>"<code in quotes>"
:
str(chr 34);(fn x=>print(x^it^x^it))"str(chr 34);(fn x=>print(x^it^x^it))"
и один в форме "<code in quotes>"<code>
:
";str(chr 34)^it;print(it^it)";str(chr 34)^it;print(it^it)
Оба полагаются на тот факт, что <code>
-part не содержит кавычек и, следовательно, может быть заключен в кавычки без необходимости избегать чего-либо,"
необходимые для вывода квинуса определяютсяstr(chr 34)
.
Они также сильно зависят от неявного идентификатора. it
который используется, когда в объявлении не указан явный идентификатор.
В первой квине str(chr 34);
связывается it
со строкой, содержащей "
, fn x=>
запускает анонимную функцию, принимающую один аргумент x
, затем объединяет x^it^x^it
и печатает полученную строку. Эта анонимная функция напрямую применяется к строке, содержащей программный код, поэтому конкатенация x^it^x^it
дает<code>"<code>"
.
Вторая квинна начинается только с кода программы в виде строки, с ";str(chr 34)^it;print(it^it)";
которой связано it
. Затем str(chr 34)^it;
конкатенирует кавычку с началом строки и, поскольку опять же не указан явный идентификатор, результирующая строка "<code>
связывается с it
. Наконец print(it^it)
соединяет строку с собой"<code>"<code>
которая затем печатается.
объяснение
Редактировать: больше не в курсе 108-байтовой версии, однако вы можете понять это и после прочтения этого объяснения.
Quine-safe quine сочетает в себе оба вышеуказанных подхода и сам по себе имеет форму "<code>"<code>
. Поместив это снова в кавычки доходности""<code>"<code>"
, мы получим пустую строку, а затем квинус другой формы.
Это означает, что программа либо получает свой собственный источник в форме "<code>
с помощью идентификатора it
, либо it
является справедливой, "
и мы получаем наш собственный источник в <code>
качестве аргумента и, следовательно, должны быть функцией, которая обрабатывает такой аргумент.
(if size it>1then(print(it^it);fn _=>())else fn x=>print(it^it^x^it^x^it))
Чтобы определить, в каком случае мы находимся, мы проверяем, больше ли размер it
единицы, чем 1. Если нет, то it
есть, "
и мы находимся во втором случае, поэтому else
-part возвращает анонимную функцию, fn x=>print(it^it^x^it^x^it)
которая затем вызывается, потому что за ней следует источник в виде строки. , Обратите внимание на начало, it^it^
которое необходимо для пустой строки в начале программы.
Если size it
больше 1, мы находимся в then
-part и просто выполняем print(it^it)
, верно? Не совсем, потому что я не сказал вам, что SML строго типизирован, что означает, что у условного if <cond> then <exp_1> else <exp_2>
выражения всегда должен быть один и тот же тип, что опять же означает, что выражения <exp_1>
и <exp_2>
должны иметь одинаковый тип. Мы уже знаем тип else
детали: анонимная функция, которая принимает строку и затем вызывает, print
имеет тип string -> <return type of print>
и print
тип string -> unit
( unit
в некотором роде похожа на void
другие языки), поэтому результирующий тип снова string -> unit
.
Таким образом, если бы then
деталь была просто print(it^it)
с типом unit
, мы получили бы ошибку несоответствия типов. Так как насчет fn _=>print(it^it)
? ( _
это подстановочный знак для неиспользуемого аргумента). Эта анонимная функция сама по себе имеет тип, 'a -> unit
где 'a
обозначает произвольный тип, поэтому в контексте нашего условного выражения, обеспечивающего string -> unit
тип, это будет работать. (Переменная типа 'a
создается с помощью типа string
.) Однако в этом случае мы не будем ничего печатать, так как анонимная функция никогда не вызывается! Помните, что когда мы заходим в then
-part, общий код таков "<code>"<code>
, что<code>
-part оценивает функцию, но, поскольку после нее ничего не происходит, она не вызывается.
Вместо этого мы используем секвенизацию, которая имеет вид (<exp_1>; ...; <exp_n>)
где<exp_1>
к <exp_n-1>
может иметь произвольные типы и тип <exp_n>
обеспечивает тип всей sequentialisation. С функциональной точки зрения значения <exp_1>
to <exp_n-1>
просто отбрасываются, однако SML также поддерживает императивные конструкции, поэтому выражения могут иметь побочные эффекты. Короче говоря, мы принимаем (print(it^it);print)
в качестве then
-part, печатая сначала, а затем возвращая функцию print
правильного типа.