To wyzwanie jest oparty off Helka Homba pytanie „s Programowanie dziewiczy świat . Z tego pytania definicja nieskazitelnego programu brzmi:
Zdefiniujmy nieskazitelny program jako program, który sam nie ma żadnych błędów, ale spowoduje błąd, jeśli zmodyfikujesz go, usuwając ciągłe podciągi N znaków, gdzie
1 <= N < program length
.Na przykład trzyznakowy program w języku Python 2
`8`
jest nieskazitelnym programem ( dzięki, Sp ), ponieważ wszystkie programy powstałe w wyniku usunięcia podciągów o długości 1 powodują błędy (w rzeczywistości błędy składniowe, ale zrobi to każdy rodzaj błędu):
8` `` `8
a także wszystkie programy wynikające z usunięcia podciągów o długości 2 powodują błędy:
` `
Gdyby na przykład
`8
program nie zawierał błędów`8`
, nie byłby nieskazitelny, ponieważ wszystkie wyniki usuwania podciągów muszą zawierać błędy.Uwagi:
- Ostrzeżenia kompilatora nie są liczone jako błędy.
- Błędne podprogramy mogą pobierać dane wejściowe lub dane wyjściowe lub robić cokolwiek innego, o ile nie będą w ogóle błądzić.
Twoim zadaniem jest stworzenie programu o niezerowej długości, który dokładnie wypisuje własny kod źródłowy, przestrzega zasad właściwego quine i jest nieskazitelny.
Najkrótsza odpowiedź w bajtach dla każdego języka wygrywa.
Odpowiedzi:
Haskell , 132 bajty
Wypróbuj online!
To jest przedłużenie quine
który działa poprzez połączenie łańcucha danych z cytowaną wersją (przy użyciu
show
) samego siebie i wydrukowaniem wyniku. Nie jest to jednak nieskazitelne, ponieważ dowolne znaki w ciągu danych można usunąć bez awarii, a także$(++)<*>show$
lub(++)<*>
część część można upuścić bez zerwania programu.Aby to naprawić,
q
zdefiniowano niestandardową funkcję drukowania, która sprawdza długość danego ciągu i wywołuje,fail
jeśli jest on krótszy niż 132. Łapie to usunięcie dowolnej sekwencji z ciągu danych, a także usunięcie$(++)<*>show$
lub(++)<*>
, jak w obu przypadkach, wynikowego ciąg przekazany doq
jest krótszy.W
q
liczba132
może zostać skrócony do1
,13
,32
lub2
, w każdym przypadku, ale znowufail
jest tzw.O ile wiem, usunięcie jakiegokolwiek innego podciągu powoduje błąd składni lub typu, więc program nawet się nie kompiluje. (Przydaje się tutaj ścisły system typowania Haskella.)
Edytować: Podziękowania dla Ørjana Johansena i Shelvacu za wskazanie wad!
źródło
fail[]|length x/=122
można go usunąć.fail[]:[putStr x|length x==122]
może działać lepiej.|length x==122
można go usunąć.if length x==122 then putStr x else fail[]
być może?if then else
wcześniej, ale pomyślałem, że mogę go skrócić.putStr x
może się staćp x
, co kiedy próbowałem na moim systemie działało bardzo długo, zanim go zabiłem, podejrzewam, że rekurencja wywołania ogona została zoptymalizowana, więc jest to nieskończona pętla. Nie wiem wystarczająco dużo haskell, aby dać jakieś sugestie, jak to naprawić.p
naq
powinna to naprawić.Python 3 , 113 bajtów
Wypróbuj online!
Jak to działa
Nie możemy łatwo użyć wielu instrukcji, ponieważ drugą można usunąć, więc zaczynamy od quine z jednym wyrażeniem:
Aby zabezpieczyć go przed usunięciem podciągów, używamy
open(1,"w").write
zamiastprint
. W Pythonie 3write
zwraca liczbę napisanych znaków, które sprawdzimy,113
aby upewnić się, że żadna część łańcucha nie została usunięta. Robimy to, sprawdzając wartość zwracaną w słowniku{113:[]}
i zapętlając wynikfor[]in…:a
, co zakończy się niepowodzeniem, jeśli nie otrzymamy pustej iterowalnejfor
instrukcji lub jeśli instrukcja zostanie usunięta.źródło
Rubinowy, 78 bajtów
Napisałem to, gdy pomyślałem o wyzwaniu, aby upewnić się, że jest możliwe. Wykorzystuje to samo „opakowanie” z jednej z moich odpowiedzi na oryginalne nieskazitelne wyzwanie.
Wyjaśnienie:
eval(*[
expr])
Ocenia to, co kod zwraca jako program ruby. To skutecznie sprawdza, czy ciąg zwracany przez kod jest poprawnym programem ruby. Dogodnie, programy ruby mogą być puste lub składać się wyłącznie z białych znaków.
Operator „splat”
*
pozwala na użycie tablicy jako argumentów funkcji. Oznacza to również, że jeślieval
zostanie usunięty, wynikowym programem jest(*[
expr])
, co nie jest poprawnym ruby.($>.write(
str)-78).chr
$>
jest krótką zmienną dla STDOUT.$>.write(foo)
zapisuje foo do STDOUT i, co ważne dla tego kodu, zwraca liczbę zapisanych bajtów.$>.write(foo)-78
: Oto78
długość programu, a więc jeśli program nie jest zniekształcony, będzie to także liczba zapisanych bajtów. Dlatego w niezmienionym przypadku zwróci zero.num.chr
zwraca liczbę jako znak, np.0.chr
zwróci ciąg zawierający pojedynczy bajt zerowy. W niezmodyfikowanym programie da to ciąg z pojedynczym bajtem pustym doeval
, co jest poprawnym programem ruby, który nie ma op-op.Ponadto w programie można usunąć podłańcuch taki, że jest po prostu
eval(*[(78).chr])
lubeval(*[(8).chr])
, co oznacza, że stała liczbowa nie może kończyć się żadną z liczb (0, 4, 9, 10, 11, 12, 13, 26, 32, 35, 48 , 49, 50, 51, 52, 53, 54, 55, 56, 57, 59, 64, 95), ponieważ są to kody ascii dla prawidłowych jednoznakowych programów ruby.%{
str}
Jest to mniej znana składnia literałów łańcuchowych w Rubim. Powodem tego jest to, że w ciągu
{}
mogą być użyte zrównoważone pary , co oznacza, że ta składnia może się zawierać. Na przykład%{foo{bar}}
jest taki sam jak"foo{bar}"
.(s=%{
dane})%s
To definiuje zmienną
s
która jest danymi tego quine, jako ciąg printf.Zadania w ruby zwracają to, co zostało przypisane, więc jest to to samo, co najpierw przypisanie,
s
a następnie uruchomienies%s
%
na sznurku znajduje się cukier syntetyczny dla rubinowego odpowiednika sprintf. The%s
oznacza gdzie w danych same dane powinny być osadzone.Ten bit kodu definiuje część danych quine i osadza ją w sobie, aby utworzyć pełny kod.
źródło
Standardowy ML (MLton) ,
204182189 bajtówWypró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 kluczowymivar
ifun
(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 takievar _=print"Hello"
można skrócić do tylkovar _="Hello"
(lub nawetvar _=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>
aleval <pattern> = <expression>
gdzie wzorzec może składać się z nazw zmiennych, stałych i konstruktorów. Ponieważprint
funkcja ma typstring -> unit
, możemy użyć mecz wzór naunit
-value()
egzekwować że funkcja drukowania jest faktycznie stosowane do napisu:val()=print"Hey"
. Przy takim podejściu usunięcie jednegoprint
lub"Hey"
powodujePattern 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ówcode
poprzedzających: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 wszystkiegoval()=
w parę w nawiasach, jednak kod można zredukować doval()=()
. 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 cielefn
abstrakcjiprint(...)
. 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:
Stosując podstęp golfa z użyciem nazwy zmiennych takich jak operator
!
,$
i%
zamiastn
,t
iu
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 prostusize t
.Edycja 2: Podziękowania dla Andersa Kaseorga za inne podejście quine i wskazanie „podatności”.
źródło
"\""
bezpośrednio i używającString.toString
do zmiany znaczenia."
, wytwarzając puste wyjście ( TIO ).let ... in ... end
."
program i wydaje się, że błąd został naprawiony w tym zatwierdzeniu , więc może twój 182 lub mój 180 jest w porządku, o ile podasz niepublikowaną wersję Git MLton.