Wskazówki dotyczące pisania quinesów

30

to program, który daje moc, która jest identyczna do kodu źródłowego programu. Na tej stronie internetowej zasadniczo dbamy tylko o odpowiednie quinesy (w chwili pisania tego tekstu obecna definicja brzmi: „część danych wyjściowych jest kodowana przez inną część programu”).

Jakie masz porady na temat pisania odpowiednich quin lub programów o właściwościach podobnych do quine? Jak zwykle każda wskazówka powinna zawierać inną odpowiedź.


źródło

Odpowiedzi:

14

Użyj, evalaby zmniejszyć potrzebę kopiowania kodu

Większość quinesów wymaga dwóch kopii kodu; jeden do wykonania, drugi jako dane. Może to w rezultacie podwoić długość kodu źródłowego, co utrudni utrzymanie i pogorszy wynik, jeśli piszesz quine na zawody w .

Połączenie dwóch kopii oznacza, że ​​jedna informacja musi być wykorzystana do dwóch celów. Próba potraktowania kodu jako danych często nie jest możliwa i zwykle uważa się go za oszustwo. Traktowanie danych jako kodu może jednak odbywać się w wielu językach za pomocą wbudowanego, zwykle nazywanego eval. Jako taka, twoja quine zasadniczo polega na przechowywaniu głównej części twojej quine w zmiennej (abyś mógł odwoływać się do niej więcej niż raz), a następnie ocenieniu tej zmiennej.

Oto przykład tego, jak to działa (przykład jest napisany w Pythonie, ale coś podobnego działa w wielu innych językach):

d='print("d="+repr(d)+"\\neval(d)")'
eval(d)

źródło
2
@QPaysTaxes: Staram się, aby mój kod był tak czytelny i łatwy do utrzymania, jak pozwala na to warunek zwycięstwa. Niestety nadal jest to zazwyczaj nie do odróżnienia od szumu linii ASCII (lub zwykłego szumu linii, jeśli używam Jelly) od ludzi, którzy nie są przyzwyczajeni do języka.
14

Skorzystaj z formatowania ciągów

Jednym z najprostszych sposobów utworzenia quine jest zdefiniowanie łańcucha, a następnie umieszczenie łańcucha w sobie za pomocą formatowania łańcucha.

s='s=%r;print s%%s';print s%s

W tym przykładzie quine Python deklarujemy ciąg z pierwszą częścią równą temu, co jest przed ciągiem s=, następnie pozwalamy na wstawienie ciągu z formatowaniem za pomocą %r, a na końcu umieszczamy to, co następuje po ciągu, aby go wydrukować i sformatować . Końcowy printznak nowej linii jest wyświetlany, ponieważ drukuje znak końca nowej linii.

Tak więc szablon jest taki w Pythonie:

<A>'<A>%r<B>'<B>

Aby rozwinąć istniejącą quine o więcej kodu:

<more A>s='<more A>s=%r;print s%%s<more B>';print s%s<more B>
mbomb007
źródło
9

Funkcje stratyfikowane

W kilku językach obiekty funkcyjne (lub równoważne konstrukcje) domyślnie przechowują swój kod źródłowy i zwracają go po przekształceniu w ciąg. Pozwala to na kompaktowe quiny bez użycia ciągów znaków . Znanym przykładem takiego języka jest JavaScript:

function f(){console.log(f+"f()")}f()

Ten kod definiuje i wywołuje funkcję, fktóra po wywołaniu drukuje własny kod źródłowy, a następnie wywołuje samą funkcję. Jedyną częścią programu, która musi zostać powielona, ​​jest wywołanie funkcji f(). Oczywiście ciało funkcji może zawierać dowolny „ładunek” kodu, który będzie również wykonywany po wywołaniu funkcji.


Bardziej kompaktowa wersja tej samej sztuczki działa w golfowym języku GolfScript :

{".~"}.~

i CJam :

{"_~"}_~

Każda z tych quinesów najpierw definiuje anonimowy blok kodu (ujęty w nawiasy klamrowe), który zachowuje się podobnie jak obiekt funkcji w JavaScript: można go wykonać, a jeśli jest zszeregowany, zwraca kod źródłowy. Reszta kodu ( .~w GolfScript lub _~w CJam) następnie wykonuje blok, pozostawiając jego kopię na stosie. Kod wewnątrz bloku wypycha następnie ciąg znaków na stos, który powtarza kod poza blokiem. Kiedy interpreter kończy pracę, automatycznie struuje i drukuje wszystko, co pozostało na stosie. Podobnie jak w przykładzie z JavaScriptem, te bloki kodu mogą być również przystosowane do przenoszenia i wykonywania dowolnego ładunku dodatkowego kodu bez konieczności jego duplikowania.

Ilmari Karonen
źródło
9

Użyj ograniczników ciągu, które zagnieżdżają się bez ucieczki

Często jedną z najtrudniejszych części pisania pióra jest krok ucieczki. Jest to potrzebne w prawie każdym quine; problem polega na tym, że jakoś przechowujesz dane i musisz zreplikować kod, który przechowuje dane na wyjściu quine. Ten kod będzie zawierał znak ucieczki danych, więc program zobaczy formularz nieskalowany i będziesz musiał go ponownie uciec.

Najłatwiejszym sposobem obsługi kroku odwrotnego jest to, czy znaki ucieczki i nieskalowane danych różnią się tylko obecnością lub brakiem ograniczników ciągu. Ucieczka jest więc prostą kwestią dodania nowej pary ograniczników ciągu wokół ciągu. Niestety, może to oczywiście działać tylko wtedy, gdy same ograniczniki ciągu mogą być wyrażone w danych bez ucieczki.

Perl jest dobrym przykładem języka, w którym ta sztuczka działa. Mimo że jego zwykłymi ogranicznikami łańcuchowymi są "…"lub '…', rzadziej używane są q(…)gniazda, co pozwala na napisanie tego rodzaju quine:

$_=q($_=q(0);s/\d/$_/;print);s/\d/$_/;print

To jest kod + quine danych. s///jest operacją zastępowania łańcucha wyrażeń regularnych; używamy 0jako znacznika i dopasowujemy go do wyrażenia regularnego jako \d(„dowolną cyfrę”), aby uniknąć używania znacznika więcej niż jeden raz (chociaż jako kolejna optymalizacja, moglibyśmy po prostu użyć go 0ponownie, ponieważ Perl s///zastępuje tylko pierwsze wystąpienie przez domyślna). Zauważ, że nie jest tu potrzebny żaden wyraźny krok ucieczki, ponieważ q(…)ograniczniki można po prostu dosłownie włączyć do ciągu danych.


źródło
8

Kod + quines danych

Najbardziej ogólna struktura quine wygląda mniej więcej tak:

data = " ucieczkowa wersja całego programu,
        z tym ciągiem zastąpionym znacznikiem „
program = data.replace (
  wyrażenie, które ocenia znacznik, ale o nim nie wspomina ,
  uciekł (dane))
program do drukowania;

Ta struktura może być używana do pisania (dość naiwnego) quinu w większości języków. Jednak na większości systemów oceniania ma tendencję do dość słabej oceny, ponieważ musisz napisać cały program dwa razy. Jednak większość struktur quine można uznać za optymalizacje tego.

Istnieją pewne subtelności tego. W niektórych językach najtrudniejszą częścią wykonania tej operacji jest napisanie kodu ucieczki; w wielu językach wyprodukowanie znacznika bez podania jego nazwy jest trudne; a w niektórych ezoterycznych językach będziesz musiał wymyślić swój własny dosłowny ciąg. Jednak wszystkie trzy operacje nie powodują zbyt dużych problemów.

Na przykład, możemy napisać quinel Pythona, który ucieka z ciągu, używając repri wykorzystując 2-znakowy x"ciąg sekwencji (który jest reprezentowany jako "x\"", tj. Nie używając sekwencji x"w reprezentacji ciągu samego łańcucha) jako znacznik:

d='d=x"\nprint(str.replace(d,"x\\"",repr(d)))'
print(str.replace(d,"x\"",repr(d)))

źródło
2
Warto zauważyć (być może w innej odpowiedzi), że wstawienie łańcucha w pozycję znacznika jest często drogie w esolangach i warto tak ustrukturyzować kod, aby sam łańcuch był pierwszą lub ostatnią rzeczą (być może oddzieloną od koniec jednym lub dwoma znakami, które możesz zakodować na stałe), abyś wiedział, dokąd musi iść.
Martin Ender,
@MartinEnder: Zgadzam się, że warto o tym wspomnieć, ale prawdopodobnie jest to inna odpowiedź (zamiast komentarza lub edycji w tej odpowiedzi). Większość wskazówek dotyczących quine jest modyfikacją tej ogólnej struktury, dlatego najpierw chciałem ją przedstawić jako wskazówkę, ponieważ wiele osób nie ma pojęcia, od czego zacząć pisać napis.
Alternatywą dla markera jest użycie dwóch łańcuchów, zrobiłem to dla Glass .
Ørjan Johansen
4

Wykorzystaj owijanie kodu źródłowego

W wielu językach (głównie językach 2D) kod źródłowy może się owijać; pod pewnymi warunkami (np. w Befunge-98, jeśli twój program jest jednowierszowy) przekroczenie końca programu zabierze Cię z powrotem do początku programu. Ten rodzaj nieliniowego zachowania oznacza, że ​​możesz jednocześnie pisać kod, który jest wewnątrz i na zewnątrz literału łańcucha; niedopasowany "(lub cokolwiek to jest ogranicznik łańcucha) skutecznie da ci ciąg zawierający całą resztę programu (z wyjątkiem "samego).

Jednym z problemów związanych z używaniem tej sztuczki jest to, że otrzymasz ciąg widziany z punktu widzenia " , a nie od początku programu (tak jak chcesz). Jako taki, prawdopodobnie najłatwiej jest zmienić układ programu tak, aby "pojawiał się na początku lub na końcu. Często oznacza to podzielenie programu na wiele części i wykorzystanie dowolnych interesujących / nietypowych poleceń kontroli przepływu w twoim języku (większość języków, w których literały ciągów owijają się wokół programu, ma ich duży wybór).

Dobrym przykładem jest quine Befunge-98 @ Justina :

<@,+1!',k9"

Niedopasowany "na końcu programu otacza cały program literałem łańcuchowym, więc (biegną od prawej do lewej ze względu <na początek) wszystko, co musimy zrobić, to wypisać program ( 9k), a następnie wypisać podwójny cudzysłów ( '!1+,) i exit ( @). Pozwala to zaoszczędzić dwie kopie programu (jedną jako kod, drugą jako dane); dwie kopie to ten sam fragment kodu, po prostu interpretowany na różne sposoby.


źródło
4

Zapamiętaj strukturę Quine

Lubię myśleć o quinach jako o trzech częściach, a nie o 2:

  • Część 1: Wygeneruj reprezentację danych części 2 i 3.
  • Część 2: Użyj danych, aby algorytmicznie wydrukować część 1.
  • Część 3: Dekodowanie reprezentacji w celu wydrukowania części 2 i 3.

To może ułatwić myślenie o quinach. Oto quine Python, z każdą linią odpowiadającą części:

s = "print(\"s = \" + repr(s))\nprint(s)"
print("s = " + repr(s))
print(s)

Czasami używasz eval podobnego lub podobnego do usuwania duplikacji, ale ogólnie stwierdziłem, że pomaga to w pisaniu prostych quines.

Rzućmy okiem na dwa różne quines niedociążenia. To jest pierwszy:

(:aSS):aSS

Pierwsza część to (:aSS)generowanie reprezentacji danych. Drugi to :aS, który drukuje (:aSS). Trzecia część to Sdrukowanie:aSS .

Oto drugi quine:

(:aS(:^)S):^

Z początku wydaje się, że to nie pasuje. Ale jeśli rozszerzysz quine, otrzymasz:

(:aS(:^)S):aS(:^)S

Teraz (:aS(:^)S)jest część 1, :aSczęść 2 i (:^)Sczęść 3.

Esolanging Fruit
źródło