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>"
экранированная строка из code
before:
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 за другой подход к квине и указание на «уязвимость».