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)"]
Wypróbuj online!
W przypadku MLton pełne programy SML są albo wyrażeniami ograniczonymi i zakończonymi ;
(np. print"Hello";print"World";
) Lub deklaracjami ze słowami kluczowymi var
i fun
(np. var _=print"Hello"var _=print"World"
), Gdzie _
znajduje się symbol wieloznaczny, który można również zastąpić dowolną nazwą zmiennej.
Pierwsza opcja jest bezużyteczna dla nieskazitelnego programowania, ponieważ ;
sama w sobie jest poprawnym programem (który nic nie robi, ale też nie popełnia błędów). Problem z drugim podejściem polega na tym, że deklaracje takie var _=print"Hello"
można skrócić do tylko var _="Hello"
(lub nawet var _=print
), ponieważ deklaracja zvar
działa tak długo, jak długo prawa strona jest prawidłowym wyrażeniem lub wartością SML (SML jest językiem funkcjonalnym, więc funkcje mogą być używane również jako wartości).
W tym momencie byłem gotowy ogłosić, że nieskazitelne programowanie w SML jest niemożliwe, kiedy przez przypadek natknąłem się na dopasowanie wzorca w val
deklaracjach. Okazuje się, że składnia deklaracji nie jest, val <variable_name> = <expression>
ale val <pattern> = <expression>
gdzie wzorzec może składać się z nazw zmiennych, stałych i konstruktorów. Ponieważ print
funkcja ma typ string -> unit
, możemy użyć mecz wzór na unit
-value ()
egzekwować że funkcja drukowania jest faktycznie stosowane do napisu: val()=print"Hey"
. Przy takim podejściu usunięcie jednego print
lub "Hey"
powoduje Pattern and expression disagree
błąd.
Mając ten nieskazitelny druk pod ręką, następnym krokiem jest napisanie quine, zanim w końcu trzeba będzie dodać jeszcze więcej opcji ochrony przed zapisem. Wcześniej korzystałem z łatwej techniki quine SML (patrz historia wersji ), ale Anders Kaseorg wskazał inne podejście, które może zaoszczędzić trochę bajtów w jego przypadku. Używa wbudowanej String.toString
funkcji do obsługi łańcucha znaków ucieczki i ma ogólną formę <code>"<data>"
, gdzie "<data>"
jest łańcuchem znaków code
poprzedzających:
val()=(fn s=>print(s^"\""^String.toString s^"\""))"val()=(fn s=>print(s^\"\\\"\"^String.toString s^\"\\\"\"))"
To działa quine, ale jeszcze nieskazitelne. Przede wszystkim Anders Kaseorg odkrył, że MLton akceptuje pojedynczy cytat "
jako kod bez powodowania błędów, co oznacza, że nie możemy mieć kodu kończącego się cytatem jak powyżej. Najkrótszym sposobem, aby temu zapobiec, jest zawinięcie wszystkiego val()=
w parę w nawiasach, jednak kod można zredukować do val()=()
. Drugim najkrótszym sposobem, jaki znalazłem, jest użycieval()=hd[ ... ]
, tzn. Zawijamy wszystko do listy i zwracamy jej pierwszy element, aby uszczęśliwić sprawdzanie typów.
Aby upewnić się, że żadna część ciągu danych nie może zostać usunięta bez zauważenia, val
ponownie przydaje się dopasowywanie wzorca w -deklaracjach: Długość końcowego ciągu do wydrukowania (a zatem długości programu) powinna wynosić 195, więc zamiast tego możemy pisać let val t=... val 195=size t in print t end
w ciele fn
abstrakcji print(...)
. Usunięcie części ciągu powoduje, że długość jest mniejsza niż 189, powodując w ten sposóbBind
wyjątku.
Pozostaje jeszcze problem: cały val 195=size t
czek można po prostu porzucić. Możemy temu zapobiec, rozszerzając czek, aby pasował do krotki: val t=... val(216,u)=(n+size t,t)in print u end
tak, że usunięcie czeku powoduje powstanie niezwiązanej zmienneju
.
W sumie daje to następujące 195 bajtowe rozwiązanie:
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)"]
Stosując podstęp golfa z użyciem nazwy zmiennych takich jak operator !
, $
i %
zamiast n
, t
i u
aby zaoszczędzić trochę spacji (patrz tę końcówkę ) prowadzi do ostatecznej wersji 182 bajtów.
Wszystkie inne usunięcia podciągów, które nie zostały wyraźnie określone w objaśnieniu, powinny skutkować błędem składni lub typu.
Edycja 1: length(explode t)
jest po prostu size t
.
Edycja 2: Podziękowania dla Andersa Kaseorga za inne podejście quine i wskazanie „podatności”.