99 (wymawiane jako „dziewięćdziesiąt dziewięć”) to nowy ezoteryczny język programowania (nie mylić z 99 , zwróć uwagę kursywą). Twoim zadaniem w tym wyzwaniu jest napisanie możliwie najkrótszego tłumacza na 99 . Zgłoszenie z najmniejszą liczbą bajtów wygrywa. Tiebreaker przechodzi do przesłanego posta jako pierwszego.
Ponieważ to pytanie jest nieco bardziej dogłębne niż zwykle i nie mogę się doczekać dobrych odpowiedzi, przyznam nagrodę w wysokości 250 powtórzeń za moją ulubioną odpowiedź (niekoniecznie zwycięzcę).
99 Spec
99 jest językiem imperatywnym . Każda linia w programie 99 jest pojedynczą instrukcją , a podczas wykonywania wskaźnik instrukcji rozpoczyna się w górnym wierszu i przechodzi kolejno przez kolejne wiersze, wykonując je po drodze. Program kończy się po wykonaniu ostatniego wiersza. Instrukcje Goto mogą przekierowywać ścieżkę wskaźnika instrukcji.
Nowa linia, spacja i 9
to jedyne trzy znaki, które mają znaczenie w programie 99 . Wszystkie pozostałe postacie są całkowicie ignorowane. Ponadto końcowe spacje w każdej linii są ignorowane, a wiele spacji w rzędzie jest odczytywanych jako jedna spacja. („Newline” odnosi się do każdego typowego kodowania podziału linii . Nie ma znaczenia, którego używa Twój tłumacz.)
Więc ten program:
9 BLAH 99 9a9bb9c9
9 this line and the next have 6 trailing spaces 9
Jest identyczny z tym programem:
9 99 9999
9 9
Zmienne
Wszystkie zmienne w 99 mają nazwy, które są jednym lub więcej 9
ciągiem razem ( 9+
w wyrażeniu regularnym). Na przykład 9
, 99
i 9999999999
są różne zmienne. Oczywiście istnieje nieskończenie wiele (z wyjątkiem ograniczeń pamięci).
Wartość każdej zmiennej jest liczbą całkowitą z dowolną precyzją ze znakiem . Domyślnie każda zmienna jest przypisana do własnej reprezentacji numerycznej. Więc jeśli nie zostało to ponownie przypisane, wartością zmiennej 9
jest liczba 9, a wartością zmiennej 99
jest liczba 99 i tak dalej. Możesz myśleć o tym jako o traktowaniu zmiennych jako liczb zwykłych, dopóki nie zostaną one wyraźnie przypisane.
Będę używał, V
aby odnieść się do dowolnej nazwy zmiennej poniżej.
Każda instancja V
może zostać zastąpiony 9
, 99
, 999
, 9999
, itd.
Sprawozdania
Istnieje pięć różnych typów instrukcji w 99 . Każda linia w programie 99 zawiera dokładnie jedną instrukcję.
Opisana tutaj składnia zakłada, że wszystkie obce znaki zostały usunięte, wszystkie końcowe spacje zostały usunięte, a wszystkie sekwencje wielu spacji zostały zastąpione pojedynczymi spacjami.
1. Brak operacji
Pusta linia to brak operacji . Nie robi nic (oprócz zwiększania wskaźnika instrukcji).
2. Wyjście
V
Pojedyncza zmienna V
w linii drukuje tę zmienną na standardowe wyjście.
Jeśli V
ma nieparzystą liczbę 9
( 9
, 999
itp.), V
To zostanie wydrukowana wartość całkowita podzielona przez 9 (w systemie dziesiętnym).
Jeżeli V
ma parzystą liczbę 9
( 99
, 9999
itd.) , To zostanie wydrukowany znak ASCII z kodem V
podzielonym przez 9, mod 128. (To jest (V / 9) % 128
wartość od 0 do 127.)
Przykład : program
9
9999
wydrukuje 1W
. Pierwszy wiersz jest drukowany, 1
ponieważ 9/9 to 1. Drugi wiersz jest drukowany, W
ponieważ 9999/9 to 1111, a 1111 mod 128 to 87, a 87 to kod znaku W
.
Zauważ, że podziały wierszy nie są drukowane między tokenami wyjściowymi. \n
należy jawnie wydrukować w celu podziału linii.
3. Wejście
V
Pojedyncza zmienna V
w linii z wiodącą spacją pobiera dane wejściowe ze standardowego wejścia i przechowuje je w tej zmiennej.
Jeśli V
ma nieparzystą liczbę 9
, to użytkownik może wpisać dowolną liczbę całkowitą ze znakiem i V
będzie ustawiony na 9-krotność tej wartości.
Jeśli V
ma parzystą liczbę 9
, wówczas użytkownik może wpisać dowolny znak ASCII i V
będzie ustawiony na 9-krotność jego kodu znakowego.
Przykład : Biorąc pod uwagę -57
i A
jako dane wejściowe, ten program
9
9
99
99
wyszedłby -57A
. Wewnętrznie zmienna 9
miałaby wartość -513 i 99
miałaby wartość 585.
Twój tłumacz może założyć, że dane wejściowe są zawsze poprawne pod względem składniowym.
4. Przydział
To oświadczenie może być dowolnie długie. To dwie lub więcej zmiennych w linii, oddzielone spacjami:
V1 V2 V3 V4 V5 ...
Przypisuje to sumę wszystkich indeksów parzystych, minus suma indeksów nieparzystych (wyłączając ). Przypisania są wartościowe, a nie referencyjne.V1
V
V
V1
Można to przetłumaczyć na większość języków jako .V1 = V2 - V3 + V4 - V5 + ...
Jeśli więc są tylko dwie zmienne, jest to normalne przypisanie:
V1 V2
→ V1 = V2
Jeśli są trzy, to odejmuje:
V1 V2 V3
→ V1 = V2 - V3
A znak +
/ -
ciągle przełącza się z każdą dodatkową zmienną:
V1 V2 V3 V4
→ V1 = V2 - V3 + V4
Przykład : ten program wyświetli 1110123
:
999 Prints triple-nine divided by nine (111).
999 9 9 Assigns triple-nine to zero (nine minus nine).
999 Prints triple-nine divided by nine (0)
9 999 9 Assigns single-nine to negative nine (zero minus nine).
999 999 9 Adds nine to triple-nine (really subtracts negative nine).
999 Prints triple-nine divided by nine (1).
999 999 9 Adds nine to triple-nine (really subtracts negative nine).
999 Prints triple-nine divided by nine (2).
999 999 9 Adds nine to triple-nine (really subtracts negative nine).
999 Prints triple-nine divided by nine (3).
5. Idź do (skok, jeśli wszystkie zero)
To oświadczenie może być również dowolnie długie. To dwie lub więcej zmiennych w linii, oddzielone spacjami, z odstępem wiodącym :
V1 V2 V3 V4 V5 ...
Jeśli niektóre z wartości oprócz tego są niezerowe, zachowuje się to tak, jak brak operacji. Wskaźnik instrukcji jest jak zwykle przenoszony do następnego wiersza.V1
Jeśli wszystkie wartości poza tym są równe zero, wskaźnik instrukcji zostaje przesunięty na numer wiersza . Linie są indeksowane od zera, więc jeśli wynosi zero, wskaźnik przesuwa się do górnej linii. Program kończy się (zwykle bez błędów), jeśli jest ujemny lub jest większy niż najwyższy możliwy indeks (liczba linii minus jeden).V1
V1
V1
V1
Pamiętaj, że tutaj nie podzielono przez 9. A ponieważ niemożliwe jest, aby zmienna była wartością, która nie jest wielokrotnością 9, można przeskakiwać tylko numery linii, które są wielokrotnościami 9.V1
Przykłady:
Ten program wydrukuje na 1
zawsze:
9 Prints single-nine divided by nine (always 1).
99 9 9 Assigns double-nine to zero.
99 99 Jumps to line zero (top line) if double-nine is zero.
Ten program
99999999 Print G.
999 99 Set triple-nine to ninety-nine.
9999999999 9999999999 9999999999 99 99 9 9 999 999 Set 10-nine to zero.
99999999999 9999999999 Set 11-nine to zero.
999 Print triple-nine's value divided by nine. (This is the ninth line.)
99999999 Print G.
999 999 9 Subtract nine from triple-nine.
99999 999 Jump to line 5-nines if triple-nine is zero (ends program).
9 99999999999 9999999999 Jump to line nine if 10-nine and 11-nine are zero (always jumps).
wyświetli liczby od 11 do 1, w malejącej kolejności, otoczone przez G
:
G11G10G9G8G7G6G5G4G3G2G1G
Dodatkowe Szczegóły
Idealny interpreter będzie działał z wiersza poleceń z nazwą pliku programu 99 jako argumentem. We / wy będzie również wykonywane w locie w wierszu poleceń.
Możesz jednak napisać funkcję interpretera, która pobierze program jako ciąg znaków, a także listę tokenów wejściowych (np ["-57", "A"]
.). Funkcja powinna wydrukować lub zwrócić ciąg wyjściowy.
Nieco inne sposoby uruchamiania interpretera i obsługi We / Wy są w porządku, jeśli te opcje są niemożliwe w Twoim języku.
Bonus: Napisz coś fajnego w 99, a chętnie zamieszczę to w tym poście jako przykład.
- Oto Pastebin zgrabnego programu „99 butelek piwa” z odpowiedzi Maca .
Mam nadzieję, że podobało Ci się moje 99. wyzwanie! :RE
źródło
Odpowiedzi:
CJam, 157 bajtów
Wypróbuj online:
Wyjaśnienie
Próba sformatowania tego przy użyciu odpowiedniego wcięcia i komentarzy prawdopodobnie potrwa wieczność, więc przedstawię tylko algorytmiczne streszczenie.
Kod jest blokiem, analogicznym do anonimowych funkcji CJam. Blok oczekuje wykonania ciągu programu i listy danych wejściowych na stosie.
Inicjalizacja składa się z trzech etapów. Najpierw zapisywana jest lista wejść. Następnie każdy znak w programie, który nie ma znaczenia, jest usuwany, a wynik jest dzielony na listę linii i zapisywany. Na koniec inicjowana jest lista zmiennych. Ta lista odwzorowuje każdą zmienną, indeksowaną według długości nazwy, na jej wartość podzieloną przez 9 (zmienna nigdy nie może przechowywać wartości, która nie jest wielokrotnością liczby 9, a wszystkie operacje oprócz goto korzystają z tej zmiany). Lista jest inicjowana do długości najdłuższej linii, która jest górną granicą najdłuższej możliwej zmiennej nazwy. Jest także trochę niejawnej inicjalizacji z powodu początkowych wartości zmiennych: numer linii to 0, a indeks wejściowy to -1.
Interpretator jest implementowany zgodnie z oczekiwaniami: pętla, która odczytuje następną linię, zwiększa numer linii i wykonuje linię, podczas gdy numer linii wskazuje na istniejącą linię. Analiza linii najpierw sprawdza, czy linia nie jest pusta, następnie rozgałęzia się na podstawie tego, czy arity ma wartość 1 czy> 1, a następnie rozgałęzia się na podstawie tego, czy istnieje spacja wiodąca. Te cztery gałęzie naśladują cztery (z wyjątkiem braku operacji) operacje w przeważającej mierze proste, choć agresywnie gra w golfa, jak wszystko inne. Być może jedną z optymalizacji uwagi jest to, że ponieważ poprawna sekwencja wejściowa powinna zawsze generować element typu oczekiwany przez program, pominąłem tworzenie osobnych przypadków dla danych wejściowych na podstawie długości nazwy zmiennej. Zakłada się po prostu, że element odczytany z listy danych wejściowych jest oczekiwanego typu.
źródło
128%
z128,=
.Python 3,
421414410404388395401 bajtówGra w golfa:
Nie golfowany:
Prawie dosłowna implementacja specyfikacji, grałem w golfa tak daleko, jak to tylko możliwe.
Uruchom z wiersza poleceń, podając plik kodu źródłowego 99 jako jedyny argument (np. Ostatni przykład z OP):
Jako dodatkowy bonus, oto (raczej słaba) implementacja „99 butelek” w 99 : http://pastebin.com/nczmzkFs
źródło
else
po pewnej liczbie można usunąć, ale kiedy spróbowałem wcześniej, dostałem błąd składniowy. Inne wskazówki są mile widziane!goto
procedurze i podczas uzyskiwania domyślnej wartości zmiennej). Jeśli chodzi o użytkownika języka, nie ma to znaczenia.else
sama, tylko przestrzeń przed nią. Np3*n+1if n%2else n//2
.else
. Na przykład, próbowałem wymianieprint(w if L(e)%2 else chr(w%128))
zprint(w if L(e)%2else chr(w%128))
i dostał wyjątku składni.e
lubE
, i (z komentarzy) nie dla0or
żadnego z nich.Common Lisp,
1180857837836 bajtówWiem, że to nie wygra, ale bawiłem się dobrze grając w golfa. Udało mi się usunąć 343 bajty, czyli ponad dwa 99 tłumaczy napisanych w CJam.
Co więcej, dość zabawne, im bardziej próbuję go skompresować, tym bardziej jestem przekonany, że w przypadku Common Lisp krótsze jest skompilowanie kodu niż próba interpretacji go w locie.
jest jeden
tagbody
do wykonania 2 pętli:zmienne lokalne są deklarowane w
&aux
Skomentował
Podczas oceny używamy standardowego wejścia / wyjścia, co oznacza, że używamy standardu
read
iprinc
funkcji. Dlatego wynikowy kod może zostać wykonany w wierszu poleceń, jak pokazano poniżej.Dane wejściowe nie są całkowicie odkażone podczas uruchamiania 99 programów: zakłada się, że użytkownik wie, jakie wartości są oczekiwane.
Jedyny możliwy narzut podczas pracy może wystąpić podczas przeskakiwania, ponieważ musimy ocenić wartość zmiennej i dopasować tę wartość do etykiety. Poza tym tłumacz powinien być dość wydajny.
Oparta na sprytnym obsesji Maca , że nie musimy za każdym razem dzielić i mnożyć przez 9, bieżąca wersja nigdy nie dzieli ani nie mnoży przez 9 podczas wykonywania.
Przykład
Jeśli zastąpimy
defmacro
przezdefun
widzimy wygenerowany kod. Na przykład:Oto wynikowy kod:
Po uruchomieniu drukuje „G11G10G9G8G7G6G5G4G3G2G1G”
Wiersz poleceń
Możemy zbudować plik wykonywalny, zrzucając rdzeń i określając
toplevel
funkcję. Zdefiniuj plik o nazwie, wboot.lisp
której umieściszdefmacro
, a następnie napisz:Uruchomienie
sbcl --load boot.lisp
daje następujące dane wyjściowe:Następnie uruchomienie skompilowanego programu 99 :
99 butelek
Jeśli jesteś zainteresowany, oto skompilowany kod programu 99 butelek napisany w odpowiedzi Maca : http://pastebin.com/ZXe839CZ (jest to stara wersja, w której mamy
jmp
iend
etykiety, otaczającą lambda i ładniejszą arytmetykę).Oto wykonanie z nową wersją, aby udowodnić, że nadal działa: http://pastebin.com/raw.php?i=h73q58FN
źródło
TI-84 Basic (skrypt kalkulatora),
376373377381 bajtówJeśli działa na kalkulatorze TI-84, będziesz mógł go używać w standardowym teście ... więc jest to przydatne;)
Minimalna wersja systemu operacyjnego - 2,53 MP (MathPrint) ze względu na sigma sumowania
Wytyczne PS ASCII nie mogły być przestrzegane dokładnie, ale w TI-Basic
:
jest nowy wiersz. Zatem wszystkie rzeczywiste znaki nowego wiersza w kodzie oznaczają, że znak „:
lub”#
na początku każdego wiersza nie jest wymagany. Początkowe tokeny:
i#
po prostu rozróżniaj komentarze i kod.Oryginalny zrzut heksadecymalny (376 bajtów)
Edycja nr 1 - Zoptymalizowane 3 bajty za pomocą obserwacji Maca Edycja nr 2 i nr 3 - Naprawiono błędy wykryte przez Runer112.
źródło
#
, w komentarzach? (Uwaga: komentarze w rzeczywistym kodzie są zaimplementowane jako linia z tylko niezamkniętym łańcuchem, który zapycha Ans)Ans
wejściowe są nadpisywane, więcAns->Str0
w wierszu 6 wystąpi błąd, w wielu przypadkach argument długościsub()
polecenia może wynosić zero, co powoduje błąd,Ans
w wierszu 11 ciąg znaków więcAns-J
będzie błąd ... I patrzyłem tylko na pierwszą połowę programu.sub()
polecenie może mieć długość zero i zgłosić błąd. A kiedysub()
wywołania zostaną naprawione, obawiam się, że może to ujawnić więcej problemów.9
jest liczba 9, a wartością zmiennej99
jest liczba 99, i tak dalej." I łańcuchy o długości 0 mogą być wytwarzane za pomocą środków podobnych""
, ale jest to rodzaj błędu, który w zasadzie żadne polecenie manipulacji ciągiem nie może zużyć ani wytworzyć pustego łańcucha, w tymsub()
.C 426
458 481 497Edytuj Może idę za daleko, ale działa to z Visual C: usunąłem stdio.h, używając int zamiast FILE * dla fopen i getc
Edytuj 2 Krok zmiany kolejności wykonania, więcej bałaganu, zapisanych 32 znaków
Autonomiczny program konsoli, nazwa programu pobierana z wiersza poleceń i wejście / wyjście za pośrednictwem konsoli.
K&R w starym stylu, domyślny typ int dla zmiennych globalnych i parametrów. Zakładając, że EOF jest zdefiniowane jako -1 (jak to ma miejsce w każdej implementacji C, o której wiem)
Kompiluje się z ostrzeżeniami za pomocą Visual Studio 2010 (projekt C ++ konsoli Win32, kompilacja jako C) Kompiluje na Ideone, ale nie może działać, ponieważ potrzebuje pliku.
Pierwszy krok, kod źródłowy jest odczytywany i analizowany, każda linia jest przechowywana jako sekwencja liczb całkowitych w oparciu o liczby 9s. Jeśli na początku jest puste, pierwsza liczba jest ujemna. Więc:
9 BLAH 99 9a9bb9c9
(9 99 9999
) staje się-1,2,4
Istnieje skrót - nie tak legalny: wszystkie kody ascii mniejsze niż „” są uważane za znaki nowej linii.W tym kroku wszystkie używane zmienne są wstępnie inicjowane.
Krok wykonania jest zgodny ze specyfikacją, bez dodatków, zapisz zapisywanie liczb podzielonych przez 9.
Bardziej czytelny sam kod (mam nadzieję), dodane spacje i znaki nowej linii
źródło
Haskell, 550 bajtów
Przykład uruchom z programem „odliczanie” zapisanym w pliku
i.99
Wersja bez golfa:
źródło
JavaScript (ES6) 340
352Funkcja z 2 parametrami
Trzeci opcjonalny parametr (domyślnie 10k) to maksymalna liczba iteracji - nie podoba mi się program, który działa wiecznie
JSFiddle Aby przetestować
źródło
k / k,
490469.
Skrypt jest mieszanką q i k, więc najpierw definiuję kilka q słów kluczowych, których chcę używać wielokrotnie w funkcjach k. (w zasadzie # zdefiniować makra)
f
odczytuje plik przekazany do programu i usuwa niepotrzebne znakim
pobiera listę / wektor i mnoży indeksy nieparzyste przez -1b
to po prostu pusta funkcja używana w liniach no-opp
jest funkcją drukowania.K
to funkcja, która sprawdza zmienną. Jeśli zmienna istnieje, zwraca ją, w przeciwnym razie po prostu zwraca literał.v
jest funkcją przypisania.g
jest funkcją goto.r
bierze ciąg i decyduje, którą operację należy zastosować.I w końcu, po prostu iteruję
f
listę ciągów znaków, zn
iteratorem. Funkcja goto zostanie zaktualizowanan
w razie potrzeby.źródło
Perl,
273 266 255 244238Dodano podział linii dla zachowania przejrzystości.
Nazwa programu pobrana z wiersza poleceń:
Każda linia programu jest konwertowana na kod Perla, na przykład:
Więcej szczegółów
źródło