";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правильного типа.