";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))
Версія без котирування : Спробуйте це на кодування.
Версія, що цитується: Спробуйте на кодування.
Зауважте, що результат виглядає приблизно так
> 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який використовується, коли в декларації не вказано явного ідентифікатора.
У першому quine 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-байтна версія, однак ви також можете зрозуміти її, прочитавши це пояснення.
Квітна безпечна квітка поєднує в собі обидва вищезазначені підходи і є самою формою "<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і просто виконуємо 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 оцінює функцію, але, оскільки після неї нічого не відбувається, його не викликають.
Замість цього ми використовуємо sequentialisation , яка має вигляд , (<exp_1>; ...; <exp_n>)де <exp_1>до <exp_n-1>може мати довільні типи і тип <exp_n>забезпечує тип всієї sequentialisation. З функціональної точки зору значення <exp_1>для <exp_n-1>просто відкидається, однак SML також підтримує імперативні конструкції , так що вираження можуть мати побічні ефекти. Коротше кажучи, ми беремо (print(it^it);print)як then-part, таким чином спочатку друкуємо, а потім повертаємо функцію, printщо має правильний тип.