Quining nieskazitelny świat


16

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 `8program 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.


Zakładam, że języki, które nie zawierają błędów, nie mogą konkurować?
ATaco,

@ATaco Niestety tak. Inne języki, takie jak lisp, mają taką strukturę składni, że niemożliwe jest stworzenie użytecznego nieskazitelnego programu.
Shelvacu,

RIP Właściwie / Poważnie
ATaco

„Zwycięża najkrótsza odpowiedź w bajtach dla każdego języka”. Nie jestem pewien, czy krótkie określenie jest najlepszym miernikiem nieskazitelnego programu.
P. Siehr,

@ P.Siehr Co poleciłbyś zamiast tego?
Shelvacu,

Odpowiedzi:


6

Haskell , 132 bajty

q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"q x=if length x==132then putStr x else fail[];main=q$(++)<*>show$"

Wypróbuj online!

To jest przedłużenie quine

main=putStr$(++)<*>show$"main=putStr$(++)<*>show$"

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ć, qzdefiniowano niestandardową funkcję drukowania, która sprawdza długość danego ciągu i wywołuje, failjeś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 qliczba 132może zostać skrócony do 1, 13, 32lub 2, w każdym przypadku, ale znowu failjest 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!


Obawiam się, że fail[]|length x/=122można go usunąć. fail[]:[putStr x|length x==122]może działać lepiej.
Ørjan Johansen

Argh, nie, wtedy |length x==122można go usunąć. if length x==122 then putStr x else fail[]być może?
Ørjan Johansen

@ ØrjanJohansen Dobry połów, miałem if then elsewcześniej, ale pomyślałem, że mogę go skrócić.
Laikoni

2
putStr xmoż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ć.
Shelvacu,

@Shelvacu Whoops. Zmiana nazwy pna qpowinna to naprawić.
Ørjan Johansen

4

Python 3 , 113 bajtów

for[]in{113:[]}[open(1,"w").write((lambda s:s%s)('for[]in{113:[]}[open(1,"w").write((lambda s:s%%s)(%r))]:a'))]:a

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:

print((lambda s:s%s)('print((lambda s:s%%s)(%r))'))

Aby zabezpieczyć go przed usunięciem podciągów, używamy open(1,"w").writezamiast print. W Pythonie 3 writezwraca liczbę napisanych znaków, które sprawdzimy, 113aby 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 wynik for[]in…:a, co zakończy się niepowodzeniem, jeśli nie otrzymamy pustej iterowalnej forinstrukcji lub jeśli instrukcja zostanie usunięta.


1
Czy możesz wyjaśnić, jak działa Twój kod?
Shelvacu

@Shelvacu Tak, dodano.
Anders Kaseorg,

3

Rubinowy, 78 bajtów

eval(*[($>.write((s=%{eval(*[($>.write((s=%%{%s})%%s)-78).chr])})%s)-78).chr])

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śli evalzostanie 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: Oto 78dł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.chrzwraca liczbę jako znak, np. 0.chrzwróci ciąg zawierający pojedynczy bajt zerowy. W niezmodyfikowanym programie da to ciąg z pojedynczym bajtem pustym do eval, 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])lub eval(*[(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, sa 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.


3

Standardowy ML (MLton) , 204 182 189 bajtów

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 vari 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 valdeklaracjach. 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ż printfunkcja 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 printlub "Hey"powoduje Pattern and expression disagreebłą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.toStringfunkcji do obsługi łańcucha znaków ucieczki i ma ogólną formę <code>"<data>", gdzie "<data>"jest łańcuchem znaków codepoprzedzają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, valponownie 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 endw ciele fnabstrakcji 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 tczek 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 endtak, ż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, ti uaby 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”.


-2 bajty , pisząc "\""bezpośrednio i używając String.toStringdo zmiany znaczenia.
Anders Kaseorg

Czekaj, to okropne: MLton wydaje się akceptować program ", wytwarzając puste wyjście ( TIO ).
Anders Kaseorg

@AndersKaseorg Huh, to dziwne. Jednak powinno być możliwe rozwiązanie tego problemu za pomocą innego let ... in ... end.
Laikoni

@AndersKaseorg Naprawiłem problem, mam nadzieję, że nie wprowadzam nowych „luk”.
Laikoni

Właściwie przyjrzałem się programowi MLton akceptującemu "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.
Anders Kaseorg
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.