val()=hd[(fn s=>let val$ =s^"\""^String.toString s^"\"]"val(189,%)=(size$,$)in print%end)"val()=hd[(fn s=>let val$ =s^\"\\\"\"^String.toString s^\"\\\")\"val(189,%)=(size$,$)in print%end)"]
Попробуйте онлайн!
Для MLton, полные программы SML являются либо выражения с разделителями и заканчивали ;(например print"Hello";print"World";) или заявление с varи funключевыми словами (например var _=print"Hello"var _=print"World") , где _дикая карта , которая также может быть заменена любым именем переменным.
Первый вариант бесполезен для нетронутого программирования, потому что сам ;по себе является допустимой программой (которая ничего не делает, но и не выдает ошибку). Проблема со вторым подходом заключается в том, что объявления вроде var _=print"Hello"могут быть сокращены до просто var _="Hello"(или дажеvar _=print ), потому что объявление with varработает до тех пор, пока правая часть является допустимым выражением или значением SML (SML является функциональным языком, поэтому функции могут быть используется в качестве значения тоже).
В этот момент я был готов объявить невозможным программирование на SML, когда случайно наткнулся на сопоставление с образцом в val-declarations. Оказывается, что синтаксис для объявлений не является, val <variable_name> = <expression>но val <pattern> = <expression>, где шаблон может состоять из имен переменных, констант и конструкторов. Поскольку printфункция имеет тип string -> unit, мы можем использовать сопоставление с образцом на unit-value ()для обеспечения , что функция печати фактически применяется к строке: val()=print"Hey". При таком подходе, удаление либо printили "Hey"результатов в Pattern and expression disagree-ошибка.
С этим способом первичной печати под рукой, следующий шаг должен написать quine прежде, чем наконец будет добавлено еще какое-то сохранение защиты. Ранее я использовал простую технику SML quine (см. Историю изменений ), но Андерс Касорг указал на другой подход, который может сэкономить несколько байтов в его случае. Он использует встроенную String.toStringфункцию для обработки экранирования строки и имеет общий вид <code>"<data>", где "<data>"экранированная строка из codebefore:
val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"
Это рабочая хама, но еще нетронутая. Прежде всего, Андерс Касорг обнаружил, что MLton принимает одинарную кавычку "как код без ошибок, что означает, что у нас не может быть кода, заканчивающегося кавычкой, как указано выше. Кратчайший способ предотвратить это - заключить в val()=скобки все после , но затем код можно сократить до val()=(). Второй самый короткий способ, который я нашел, это использоватьval()=hd[ ... ] , то есть мы заключаем все в список и возвращаем его первый элемент, чтобы радовать проверку типов.
Чтобы убедиться, что ни одна часть строки данных не может быть удалена без valзамечаний, сопоставление с образцом в -declarations снова пригодится: длина конечной строки для печати (и, следовательно, длина программы) должна быть равна 195, поэтому мы можем написать let val t=... val 195=size t in print t endв теле fnабстракции вместо print(...). Удаление части строки приводит к длине меньше 189, что вызывает Bindисключение.
Осталась еще проблема: весь val 195=size tчек можно было просто отбросить. Мы можем предотвратить это, расширив проверку на соответствие в кортеже:, val t=... val(216,u)=(n+size t,t)in print u endтак что удаление проверки приведет к появлению несвязанной переменной u.
В целом это дает следующее 195-байтовое решение:
val()=hd[(fn s=>let val t=s^"\""^String.toString s^"\")"val(195,u)=(size t,t)in print u end)"val()=hd[(fn s=>let val t=s^\"\\\"\"^String.toString s^\"\\\")\"val(195,u)=(size t,t)in print u end)"]
Применение игры в гольф трюк с использованием имен переменных оператор , как !, $и %вместо того n, tи uдля того , чтобы сэкономить немного белого пространства (см этот отзыв ) приводит к окончательной версии 182 байт.
Все другие удаления-подстроки, которые явно не указаны в объяснении, должны приводить к синтаксической ошибке или ошибке типа.
Редактировать 1: length(explode t) просто size t.
Редактировать 2: Спасибо Anders Kaseorg за другой подход к квине и указание на «уязвимость».