Napisz tłumacza na 99

99

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 9to 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 9ciągiem razem ( 9+w wyrażeniu regularnym). Na przykład 9, 99i 9999999999są 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 9jest liczba 9, a wartością zmiennej 99jest 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ł, Vaby odnieść się do dowolnej nazwy zmiennej poniżej.
Każda instancja Vmoż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 Vw linii drukuje tę zmienną na standardowe wyjście.

Jeśli Vma nieparzystą liczbę 9( 9, 999itp.), VTo zostanie wydrukowana wartość całkowita podzielona przez 9 (w systemie dziesiętnym).

Jeżeli Vma parzystą liczbę 9( 99, 9999itd.) , To zostanie wydrukowany znak ASCII z kodem Vpodzielonym przez 9, mod 128. (To jest (V / 9) % 128wartość od 0 do 127.)

Przykład : program

9
9999

wydrukuje 1W. Pierwszy wiersz jest drukowany, 1ponieważ 9/9 to 1. Drugi wiersz jest drukowany, Wponieważ 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. \nnależy jawnie wydrukować w celu podziału linii.

3. Wejście

 V

Pojedyncza zmienna Vw linii z wiodącą spacją pobiera dane wejściowe ze standardowego wejścia i przechowuje je w tej zmiennej.

Jeśli Vma nieparzystą liczbę 9, to użytkownik może wpisać dowolną liczbę całkowitą ze znakiem i Vbędzie ustawiony na 9-krotność tej wartości.

Jeśli Vma parzystą liczbę 9, wówczas użytkownik może wpisać dowolny znak ASCII i Vbędzie ustawiony na 9-krotność jego kodu znakowego.

Przykład : Biorąc pod uwagę -57i Ajako dane wejściowe, ten program

 9
9
 99
99

wyszedłby -57A. Wewnętrznie zmienna 9miałaby wartość -513 i 99miał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.V1VVV1

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 V2V1 = V2

Jeśli są trzy, to odejmuje:

V1 V2 V3V1 = V2 - V3

A znak +/ -ciągle przełącza się z każdą dodatkową zmienną:

V1 V2 V3 V4V1 = 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 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 V1V1V1

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 1zawsze:

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.


Mam nadzieję, że podobało Ci się moje 99. wyzwanie! :RE

Hobby Calvina
źródło
9
Rozważałem głosowanie, ale twój obecny wynik to 9…
wchargin
30
@WChargin wygląda na to, że teraz będziesz musiał spróbować uzyskać 99.
trlkly
5
Na pewno jest premia za samodzielne hostingowanie (pisanie 99 tłumacza w 99 ), nie?
Gabe,
5
@Gabe Taka odpowiedź prawdopodobnie dostałaby nagrodę, ale gdyby to była jedyna odpowiedź, co interpretowałoby tłumacza? ;)
Hobby Calvina
1
@Optimizer to działa: pastebin.com/raw.php?i=h73q58FN
coredump

Odpowiedzi:

16

CJam, 157 bajtów

{:I;_N" 9"+--N/:P:,$W=){1a*Ab}%:V;{PT):T(=:LS%_{LS#\:,_,({(\{V=}%@{V-1@{2$*+0@-\}*\;t:V;}{:|T@V=9*?:T;}?}{~\{_V=\1&!{128%c}*o}{VIW):W=it:V;}?}?}R?Tg)TP,<*}g}

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.

Runer112
źródło
15
+1. Dość krótki. Czy potrafisz napisać interpreter CJam w 99 ? ;-)
rdzeń
9
@coredump *
Runer112
Cholera, mogę dostać tylko 195, a potem straciłem nadzieję i poddałem się: P
Optimizer
To nie przyjmuje poprawnego modulo podczas drukowania wartości ujemnych. Można to naprawić poprzez wymianę 128%z 128,=.
Martin Ender
26

Python 3, 421 414 410 404 388 395 401 bajtów

Gra w golfa:

import sys,re
v,i,c,g,L={},0,[re.sub('( ?)[^9]+','\\1',l).rstrip().split(' ')for l in open(sys.argv[1])],lambda i:v.get(i,int(i)//9),len
while-1<i<L(c):
 d=c[i];l=L(d);e,*f=d;i+=1
 if l>1:
  x,*y=f
  if e:w=list(map(g,f));v[e]=sum(w[::2])-sum(w[1::2])
  elif l==2:j=input();v[x]=int(j)if L(x)%2 else ord(j)
  elif~-any(g(j)for j in y):i=g(x)*9
 elif e:w=g(e);print(w if L(e)%2 else chr(w%128),end='')

Nie golfowany:

import sys, re

# Intialise variable table.
vars_ = {}
get_var = lambda i: vars_.get(i, int(i)//9)

# Parse commands.
commands=[re.sub('( ?)[^9]+','\\1',l).rstrip().split(' ') for l in open(sys.argv[1])]

# Run until the current instruction index is out of bounds.
index=0
while 0 <= index < len(commands):
    # Get the current command and increment the index.
    command = commands[index]
    l = len(command)
    first = command[0]
    index += 1

    if l > 1:
        # Handle the "assignment" command.
        if first:
            operands = [get_var(i) for i in command[1:]]
            vars_[first] = sum(operands[0::2]) - sum(operands[1::2])
        # Handle the "input" command.
        elif l==2:
            inp = input()
            vars_[command[1]] = int(inp) if len(command[1]) % 2 else ord(inp)
        # Handle the "goto" command.
        elif not any(get_var(i) for i in command[2:]):
            index = get_var(command[1]) * 9
    # Handle the "output" command.
    elif first:
        val = get_var(first)
        print(val if len(first) % 2 else chr(val % 128),end='')

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):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

Jako dodatkowy bonus, oto (raczej słaba) implementacja „99 butelek” w 99 : http://pastebin.com/nczmzkFs

Prochowiec
źródło
1
@DLosc: w odniesieniu do twojego pierwszego punktu: ja też myślałem, że elsepo pewnej liczbie można usunąć, ale kiedy spróbowałem wcześniej, dostałem błąd składniowy. Inne wskazówki są mile widziane!
Mac
3
@coredump: sposób zapisu specyfikacji, każda zmienna zawsze będzie miała wartość podzielną przez dziewięć. Uznałem za bardziej zwięzłe zezwalanie zmiennym na przyjmowanie dowolnej wartości i mnożenie / dzielenie tylko przez dziewięć w razie potrzeby (w szczególności w gotoprocedurze i podczas uzyskiwania domyślnej wartości zmiennej). Jeśli chodzi o użytkownika języka, nie ma to znaczenia.
Mac
2
Nie elsesama, tylko przestrzeń przed nią. Np 3*n+1if n%2else n//2.
DLosc
1
@DLosc: przepraszam, popełniłem błąd - naprawdę miałem na myśli przestrzeń, a nie else. Na przykład, próbowałem wymianie print(w if L(e)%2 else chr(w%128))z print(w if L(e)%2else chr(w%128))i dostał wyjątku składni.
Mac
1
Dziwne - testowałem na ideone.com i działało, ale masz rację, nie działa w rzeczywistym interpreterie Python3 (3.4.0 na Ubuntu). W tym poradniku wyjaśniono: liczba, po której następuje token alfabetyczny, działa ogólnie, ale nie dla tokenów rozpoczynających się od elub E, i (z komentarzy) nie dla 0orżadnego z nich.
DLosc
16

Common Lisp, 1180 857 837 836 bajtów

Wiem, ż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.

(defmacro g(g &aux a(~ -1)> d x q(m 0)r v(n t)c(w 0)? u z)(flet((w(n p)(intern(format()"~a~a"p n))))(#1=tagbody %(case(setf c(ignore-errors(elt g(incf ~))))(#\  #2=(when(> w 0)(pushnew w v)(if u()(setq ?(oddp w)))(#5=push(w w'V)u)(setf w 0))(setf z t))(#\9(incf w)(setf >(or >(and n z))z()n()))((#\Newline())#2#(#5#(when u(setf u(reverse u)a(pop u))(if >(if u`(when(every'zerop(list,@u))(setf @,a)(go ^))`(setf,a,(if ?'(read)'(char-code(read-char)))))(if u`(setf,a,(do(p m)((not u)`(-(+,@p),@m))(#5#(pop u)p)(#5#(if u(pop u)0)m)))`(princ,(if ? a`(code-char(mod,a 128)))))))r)(incf m)(setf ?()u()z()>()n t)))(if c(go %))$(decf m)(setq d(pop r))(if d(#5# d x))(when(=(mod m 9)0)(#5#(w #3=(/ m 9)'L)x)(#5#`(,#3#(go,(w #3#'L)))q))(if(>= m 0)(go $)))`(let(@,@(mapcar(lambda(n)`(,(w n'V),(/(1-(expt 10 n))9)))v))(#1#,@x(go >)^(case @,@q)>))))
  • analiza leksykalna i generowanie kodu są przeplatane: Nie przechowuję wewnętrznej reprezentacji, ale przetwarzam bezpośrednio każdą linię.
  • jest jeden tagbodydo wykonania 2 pętli:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • zmienne lokalne są deklarowane w &aux

  • nie generuj zamknięcia, ale bezpośrednio zinterpretowany kod
  • itp.

Skomentował

(defmacro parse-99
    (string &aux
              (~ -1) ; current position in string
              a      ; first variable in a line 
              >      ; does current line starts with a leading space?
              d      ; holds a statement during code generation
              x      ; all statements (labels + expressions)
              q      ; all generated case statements 
              (m 0)  ; count program lines (first increases, then decreases) 
              r      ; list of parsed expressions (without labels)
              v      ; set of variables in program, as integers: 999 is 3
              (n t)  ; are we in a new line without having read a variable? 
              c      ; current char in string 
              (w 0)  ; currently parsed variable, as integer 
              ?      ; is first variable odd? 
              u      ; list of variables in current line, as integers
              z)     ; is the last read token a space?
  (flet((w(n p)
          ;; produce symbols for 99 variables
          ;; e.g. (10 'V) => 'V10
          ;;      (4 'L)  => 'L4
          (intern(format()"~a~a"p n))))
    (tagbody
     parse
       (case (setf c
                   ;; read current char in string,
                   ;; which can be NIL if out-of-bounds
                   (ignore-errors(aref string (incf ~))))

         ;; Space character
         (#\Space
          #2=(when(> w 0)
               (pushnew w v)            ; we were parsing a variable, add it to "v"
               (if u()(setq ?(oddp w))) ; if stack is empty, this is the first variable, determine if odd
               (push(w w'V)u)           ; add to stack of statement variable
               (setf w 0))              ; reset w for next variable

          ;; Space can either be significant (beginning of line,
          ;; preceding a variable), or not. We don't know yet.
          (setf z t))

         ;; Nine
         (#\9
          (incf w) ; increment count of nines
          (setf >(or >(and n z)) ; there is an indent if we were
                                 ; starting a newline and reading a
                                 ; space up to this variable (or if we
                                 ; already know that there is an
                                 ; indent in current line).
                ;; reset z and n
                z()n()))

         ;; Newline, or end of string
         ((#\Newline())
          #2#  ;; COPY-PASTE the above (when(> w 0)...) statement,
               ;; which adds previously read variable if necessary.

          ;; We can now convert the currently read line.
          ;; We push either NIL or a statement into variable R.

          (push(when u
                     (setf u (reverse u) ; we pushed, we must reverse
                           a (pop u))    ; a is the first element, u is popped
                     (if >
                         ;; STARTS WITH LEADING SPACE
                         (if u
                             ;; JUMP
                             `(when(every'zerop(list,@u))(setf @,a)(go ^))

                             ;; READ
                             `(setf,a,(if ?'(read)'(char-code(read-char)))))

                         ;; STARTS WITH VARIABLE
                         (if u

                             ;; ARITHMETIC
                             `(setf,a,(do(p m) ; declare p (plus) and m (minus) lists

                                         ;; stopping condition: u is empty
                                         ((not u)
                                          ;; returned value: (- (+ ....) ....)
                                          `(-(+,@p),@m))

                                        ;; alternatively push
                                        ;; variables in p and m, while
                                        ;; popping u

                                        (push(pop u)p)

                                        ;; first pop must succeed, but
                                        ;; not necessarly the second
                                        ;; one.  using a zero when u
                                        ;; is empty covers a lot of
                                        ;; corner cases.

                                        (push(if u (pop u) 0) m)))

                             ;; PRINT
                             `(princ,(if ? a`(code-char(mod,a 128)))))))
               r)
          ;; increase line count
          (incf m)
          ;; reset intermediate variables
          (setf ?()u()z()>()n t)))

       ;; loop until end of string
       (if c (go parse))


     build
       ;;; Now, we can add labels in generated code, for jumps

       ;; decrease line count M, which guards our second loop
       (decf m)

       ;; Take generated statement from R
       (setq d(pop r))

       ;; we pop from R and push in X, which means X will eventually
       ;; be in the correct sequence order. Here, we can safely
       ;; discard NIL statements.

       ;; We first push the expression, and THEN the label, so that
       ;; the label ends up being BEFORE the corresponding statement.
       (if d(push d x))

       ;; We can only jump into lines multiple of 9
       (when (=(mod m 9)0)
         ;; Push label
         (push(w #3=(/ m 9)'L)x)
         ;; Also, build a case statement for the jump table (e.g. 2(go L2))
         (push`(,#3#(go,(w #3#'L)))q))
       ;; loop
       (if(>= m 0)(go build)))

    ;; Finally, return the code
    `(let(@ ; target of a jump instruction

          ;; other variables: V3 represents 999 and has a default value of 111
          ,@(mapcar(lambda(n)`(,(w n'V),(/(1-(expt 10 n))9)))v))

       ;; build a tagbody, inject statements from X and case statements from Q
       ;; label ^ points to jump table : we go to ^ each time there is a JUMP
       ;; label > is the end of program

       ;; note that if the case does not match any authorized target
       ;; address, we simply end the programs.
       (tagbody,@x(go >)^(case @,@q)>))))

Podczas oceny używamy standardowego wejścia / wyjścia, co oznacza, że ​​używamy standardu readi princfunkcji. 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 defmacroprzez defunwidzimy wygenerowany kod. Na przykład:

(g
"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 (endsprogram).
 9 99999999999 9999999999                             Jump to line nine if 10-nine and 11-nine are zero (alwa

")

Oto wynikowy kod:

(LET (@
      (V5 11111)
      (V11 11111111111)
      (V1 1)
      (V10 1111111111)
      (V2 11)
      (V3 111)
      (V8 11111111))
  (TAGBODY
   L0
    (PRINC (CODE-CHAR (MOD V8 128)))
    (SETF V3 (- (+ V2) 0))
    (SETF V10 (- (+ V3 V1 V2 V10) V3 V1 V2 V10))
    (SETF V11 (- (+ V10) 0))
   L1
    (PRINC V3)
    (PRINC (CODE-CHAR (MOD V8 128)))
    (SETF V3 (- (+ V3) V1))
    (WHEN (EVERY 'ZEROP (LIST V3)) (SETF @ V5) (GO ^))
    (WHEN (EVERY 'ZEROP (LIST V11 V10)) (SETF @ V1) (GO ^))
    (GO >)
   ^
    (CASE @ (0 (GO L0)) (1 (GO L1)))
   >))

Po uruchomieniu drukuje „G11G10G9G8G7G6G5G4G3G2G1G”

Wiersz poleceń

Możemy zbudować plik wykonywalny, zrzucając rdzeń i określając toplevelfunkcję. Zdefiniuj plik o nazwie, w boot.lispktórej umieścisz defmacro, a następnie napisz:

(defun main()(parse-99 <PROGRAM>))
(save-lisp-and-die "test-99" :executable t :toplevel #'main)

Uruchomienie sbcl --load boot.lispdaje następujące dane wyjściowe:

$ sbcl --load boot.lisp 
This is SBCL 1.2.8.32-18c2392, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
[undoing binding stack and other enclosing state... done]
[saving current Lisp image into test-99:
writing 5824 bytes from the read-only space at 0x20000000
writing 3120 bytes from the static space at 0x20100000
writing 55771136 bytes from the dynamic space at 0x1000000000
done]

Następnie uruchomienie skompilowanego programu 99 :

$ time ./test-99
G11G10G9G8G7G6G5G4G3G2G1G
real    0m0.009s
user    0m0.008s
sys     0m0.000s

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 jmpi endetykiety, otaczającą lambda i ładniejszą arytmetykę).

Oto wykonanie z nową wersją, aby udowodnić, że nadal działa: http://pastebin.com/raw.php?i=h73q58FN

rdzeń rdzeniowy
źródło
6

TI-84 Basic (skrypt kalkulatora), 376 373 377 381 bajtów

Jeś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

#Get input from STDIN
:Ans+":"->Str0
#Initialize instruction pointer
:1->I
#Initialize variable set
:DelVar L1999->dim(L1
#Strip out those pesky non-newline/space/9 characters
:For(J,1,length(Ans
:sub(Str0,J,1
:If not(inString(": 9",Ans
:sub(Str0,1,J-1)+sub(Str0,J+1,length(Str0)-J->Str0
:End
#Main interpreting loop
:While I<length(Str0
:sub(Str0,I+1,inString(Str0,":",I+1)-I-1->Str1
:DelVar A" "=sub(Ans,1,1->A
:inString(Str0,":",I+1->I
:If A
:sub(Str1,2,length(Str1)-1->Str1
:End
:length(Str1->L
#0 is Output, 1 is Input, 2 is Assignment, 3 is Goto
:2A+inString(Str1," ->B
:If not(Ans
:Disp L1(L
:If Ans=1
:Then
:Input C
:C->L1(L
:End
#Get those delimited variables
:If B>1
:Then
:"{"+Str1->Str2
:While inString(Ans," 
:inString(Ans," 
:sub(Str2,1,Ans-1)+sub(Str2,Ans+1,length(Str2)-Ans->Str2
:End
:log(expr(Ans)+1->L2
:End
:If B=2
#Gotta expand that -+ pattern
:Ans(2->L1(Ans(1
;Love that summation Σ
:If B=3 and Σ(L2(K),K,2,dim(L2
:Then
:DelVar IFor(K,0,9L2(1
:inString(Str0,":",I+1->I
:End
:End

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)

49 3f bb 54 5d 20 39 39 39 04 b5 5d 20 3f 72 04 aa 09 3f d3 4a 2b 31 2b bb 2b 72 3f bb 0c aa 09 2b 4a 2b 31 3f ce b8 bb 0f 2a 3e 29 39 2a 2b 72 3f bb 0c aa 09 2b 31 2b 4a 71 31 11 70 bb 0c aa 09 2b 4a 70 31 2b 72 71 4a 04 aa 09 3f d4 3f d1 49 6b bb 2b aa 09 3f bb 0c aa 09 2b 49 70 31 2b bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 11 71 49 71 31 04 aa 20 3f bb 54 41 2a 29 2a 6a bb 0c 72 2b 31 2b 31 04 41 3f bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 04 49 3f ce 41 3f bb 0c aa 20 2b 32 2b bb 2b aa 20 11 71 31 04 aa 20 3f d4 3f bb 2b aa 20 04 4c 3f 32 41 70 bb 0f aa 20 2b 2a 29 04 42 3f ce b8 72 3f de 5d 20 10 4c 11 83 39 3f ce 72 6a 31 3f cf 3f dc 43 3f 39 43 04 5d 20 10 4c 3f d4 3f ce 42 6c 31 3f cf 3f 2a 08 2a 70 aa 20 04 aa 01 3f d1 bb 0f 72 2b 2a 29 3f bb 0f 72 2b 2a 29 3f bb 0c aa 01 2b 31 2b 72 71 31 11 70 bb 0c aa 01 2b 72 70 31 2b bb 2b aa 01 11 71 72 04 aa 01 3f d4 3f c0 bb 2a 72 11 70 31 04 5d 01 3f d4 3f ce 42 6a 32 3f 72 10 32 04 5d 20 10 72 10 31 3f ce 42 6a 33 40 ef 33 5d 01 10 4b 11 2b 4b 2b 32 2b b5 5d 01 3f cf 3f bb 54 49 d3 4b 2b 30 2b 5d 01 10 31 3f bb 0f aa 09 2b 2a 3e 2a 2b 49 70 31 04 49 3f d4 3f d4 2e 76

Edycja nr 1 - Zoptymalizowane 3 bajty za pomocą obserwacji Maca Edycja nr 2 i nr 3 - Naprawiono błędy wykryte przez Runer112.

Timtech
źródło
11
Łatwość użycia w stresujących sytuacjach, takich jak standardowe testy, jest dokładnie tym , dla czego zaprojektowałem 99 .
Calvin's Hobbies
1
Czy mogę zasugerować użycie innej postaci, na przykład #, w komentarzach? (Uwaga: komentarze w rzeczywistym kodzie są zaimplementowane jako linia z tylko niezamkniętym łańcuchem, który zapycha Ans)
Riking
8
Czy rzeczywiście próbowałeś to uruchomić? Nie zrobiłem tego, ale patrząc na to trochę więcej, zauważyłem coś, co wydaje się być co najmniej pół tuzinem błędów. Na przykład: zmienne nie są inicjowane swoimi wartościami, dane Answejściowe są nadpisywane, więc Ans->Str0w wierszu 6 wystąpi błąd, w wielu przypadkach argument długości sub()polecenia może wynosić zero, co powoduje błąd, Answ wierszu 11 ciąg znaków więc Ans-Jbędzie błąd ... I patrzyłem tylko na pierwszą połowę programu.
Runer112,
1
@Timtech To jednak pozostawia inne problemy. Jak wspomniałem, brakuje we / wy znaków, brak inicjalizacji zmiennej i wiele instancji, w których sub()polecenie może mieć długość zero i zgłosić błąd. A kiedy sub()wywołania zostaną naprawione, obawiam się, że może to ujawnić więcej problemów.
Runer112,
1
@ Timtech miałem na myśli: „Domyślnie każda zmienna jest przypisana do własnej reprezentacji numerycznej. Więc jeśli nie zostanie ponownie przypisana, wartością zmiennej 9jest liczba 9, a wartością zmiennej 99jest 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 tym sub().
Runer112,
5

C 426 458 481 497

Edytuj 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

B[99999],*r,*i[9999],V[999],v,w,m,n;unsigned p,s;
main(b,a)char*a[];{r=i[0]=B;m=fopen(a[1],"r");
do if(w=getc(m),n+=w==57,w<33){
if(n){for(v=1,b=n;--b;)v=v*10+1;V[n]=v;*r++=p?-n:n;b=n=0;};
w-32?(*r=p=0,b=i[++s]=++r):(p=b,b=0);}while(w>=0);
while(p<s)if(w=0,r=i[p++],v=*r++)
if(m=v>0,*r){for(;b=*r++;m=-m)w=w+m*V[b]|!m*V[b];m?V[v]=w:(p=w?p:9*V[-v]);
}else~v&1?!m?V[-v]=getchar():putchar(V[v]&127):m?printf("%d",V[v]):scanf("%d",V-v);
}

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

B[99999],*r,*i[9999],V[999],v,w,m,n;
unsigned p,s;
main(b,a)char*a[];
{
  r=i[0]=B;
  m=fopen(a[1],"r");
  do if(w=getc(m),n+=w==57,w<33)
  {
     if(n){for(v=1,b=n;--b;)v=v*10+1;V[n]=v;*r++=p?-n:n;b=n=0;};
     w-32?(*r=p=0,b=i[++s]=++r):(p=b,b=0);
  }
  while (w>=0);
  while (p<s)
    if (w = 0, r = i[p++], v = *r++)
        if (m = v > 0, *r){
            for(; b = *r++; m = -m)
                w = w + m*V[b] | !m*V[b];
            m ? V[v]=w : (p = w ? p : 9*V[-v]);
        } else
            ~v & 1 
            ? !m ? V[-v] = getchar() : putchar(V[v] & 127)  
            : m ? printf("%d", V[v]) : scanf("%d", V - v);
}
edc65
źródło
1
Współpracuje również z GCC 4.8.2. Kompiluje jako C99!
EMBLEMAT
4

Haskell, 550 bajtów

import Data.List.Split
import System.Environment
a#b=takeWhile(/=a)b
(!)=map
main=do(f:_)<-getArgs;readFile f>>=e.(p!).lines
p l=(if ' '#l<'9'#l then[0]else[])++length!(wordsBy(/='9')l)
e l=(\x->div(10^x-1)9)%l where
 _%[]=return()
 v%([]:r)=v%r
 v%([n]:r)=putStr(if odd n then show(v n)else[toEnum$v n`mod`128])>>v%r
 v%([0,n]:r)=do i<-getLine;u n(if odd n then read i else fromEnum$head i)v%r
 v%((0:n:m):r)|any(/=0)(v!m)=v%r|v n<0=v%[]|1<2=v%drop(9*v n)l
 v%((n:m):r)=u n(sum$zipWith(*)(v!m)(cycle[1,-1]))v%r
u n i v= \x->if x==n then i else v x

Przykład uruchom z programem „odliczanie” zapisanym w pliku i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Wersja bez golfa:

import Data.List.Split
import System.Environment

-- The main function takes the first command line argument as a file name,
-- reads the content, splits it into lines, parses each line and evaluates
-- the list of parsed lines.
main = do
 (f:_)<-getArgs
 readFile f >>= eval.map parse.lines

-- each line is coverted into a list of integers, which represent the number
-- of 9s (e.g. "999 99 9999" -> [3,2,4]). If there's a space before the first
-- 9, a 0 is put in front of the list (e.g. " 9 9 999" -> [0,1,1,3]).
parse l = (if takeWhile (/=' ') l < takeWhile (/='9') l then [0] else [])
   ++ map length (wordsBy(/='9') l)

-- The work is done by the helper function 'go', which takes two arguments
--   a) a functions which takes an integer i and returns the value of the
--      variable with i 9s (e.g: input: 4, output: value of 9999). To be
--      exact, the value divided by 9 is returned.
--   b) a list of lines to work on
-- 'eval' starts the process with a function that returns i 1s for every i and
-- the list of the parsed input. 'go' checks which statement has to be
-- executed for the next line and calls itself recursively
eval list = go (\x -> div (10^x-1) 9) list
   where
   go _ []                  = return ()
   go v ([]:r)              = go v r
   go v ([n]:r)             = putStr (if odd n then show(v n) else [toEnum (v n`mod`128)]) >> go v r
   go v ([0,n]:r)           = do i<-getLine ; go (update n (if odd n then read i else fromEnum$head i) v) r
   go v ((0:n:m):r)
      | any (/=0) (map v m) = go v r
      | v n < 0             = go v []
      | otherwise           = go v (drop (9*v n) list)
   go v ((n:m):r)           = go (update n (sum $ zipWith (*) (map v m) (cycle[1,-1])) v) r

-- updates a function for retrieving variable values.
-- n = position to update
-- i = new value
-- v = the function to update
update n i v = \x->if x==n then i else v x
nimi
źródło
4

JavaScript (ES6) 340 352

Funkcja z 2 parametrami

  • kod programu jako ciąg wielowierszowy
  • dane wejściowe jako tablica

Trzeci opcjonalny parametr (domyślnie 10k) to maksymalna liczba iteracji - nie podoba mi się program, który działa wiecznie

JSFiddle Aby przetestować

I=(c,i,k=1e5,
  V=v=>v in V?V[v]:v/9 // variable getter with default initial value
)=>(c=>{
 for(p=o='';--k&&p<c[L='length'];)
   (v=(r=c[p++].split(' '))[S='shift']())? // no leading space
      r[r.map(t=>w-=(m=-m)*V(t),w=0,m=1),0]?V[v]=w // Assign
      :o+=v[L]&1?V(v):String.fromCharCode(V(v)&127) // Output
   : // else, leading space
    (v=r[S]())&&
       (r[0]?r.some(t=>V(t))?0:p=9*V(v) // Goto
       :(t=i[S](),V[v]=v[L]&1?t:t.charCodeAt()) // Input
    )
})(c.replace(/ (?=[^9])|[^9\s]/g,'').split('\n'))  // code cleaning
||o
edc65
źródło
4

k / k, 490 469

M:mod;T:trim;R:read0;S:set;s:" "
f:(rtrim')(f:R -1!`$.z.x 0)inter\:"9 \n"
k)m:{@[x;&M[!#x;2];-:]}
b:{}
k)p:{1@$$[1=M[#x;2];(K x)%9;"c"$M[(K x)%9;128]];}
k)i:{S[(`$T x);$[1=M[#T x;2];9*"J"$R 0;*9*"i"$R 0]]}
k)K:{$[#!:a:`$x;.:a;"I"$x]}
k)v:{(S).(`$*:;+/m@K'1_)@\:T's\:x}
k)g:{$[&/0=C:K'c:1_J:s\:T x;n::-1+K@*J;|/~0=C;;(d<0)|(d:*C)<#f;exit 0]}
k)r:{`b`p`i`v`g@*&(&/x=s;q&1=c;(e~s)&1=C;(q:e~"9")&1<c:#s\:x;((e:*x)~s)&1<C:#s\:1_x)}
k)n:0;while[~n>#o:(r')f;(o n)f n;n+:1]
\\

.

$ q 99.q countdown.txt -q
G11G10G9G8G7G6G5G4G3G2G1G

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)

M:mod;T:trim;R:read0;S:set

f odczytuje plik przekazany do programu i usuwa niepotrzebne znaki

q)f
"99999999"
"999 99"
"9999999999 9999999999 9999999999 99 99 9 9 999 999"
"99999999999 9999999999"
""
""
""
""
""
"999"
"99999999"
"999 999 9"
" 99999 999"
" 9 99999999999 9999999999"

m pobiera listę / wektor i mnoży indeksy nieparzyste przez -1

q)m 1 2 3 4 5
1 -2 3 -4 5

b to po prostu pusta funkcja używana w liniach no-op

p jest funkcją drukowania.

Kto funkcja, która sprawdza zmienną. Jeśli zmienna istnieje, zwraca ją, w przeciwnym razie po prostu zwraca literał.

//999 not defined, so just return 999
q)K "999"
999
//Set 999 to 9
q)v "999 9"
//K now returns 9
q)K "999"
9

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ę flistę ciągów znaków, z niteratorem. Funkcja goto zostanie zaktualizowana nw razie potrzeby.

tartin
źródło
3

Perl, 273 266 255 244 238

Dodano podział linii dla zachowania przejrzystości.

open A,pop;
for(@c=<A>){
y/ 9//cd;s/ +/ /g;s/ $//;
$p="((99)+|9+)";$a='+';
s/^ $p$/$1='$2'?ord<>:<>/;
s/^$p$/print'$2'?chr$1%128:$1/;
s/^ $p /\$_=$1*011unless/&&y/ /|/;
s/ /=/;s/ /$a=-$a/ge;
s!9+!${x.$&}=$&/9;"\$x$&"!eg}
eval$c[$_++]until/-/|$_>@c

Nazwa programu pobrana z wiersza poleceń:

$ perl 99.pl 99beers.99

Każda linia programu jest konwertowana na kod Perla, na przykład:

print'$x99'?chr$x99999999%128:$x99999999
$x999=$x99
$x9999999999=$x9999999999-$x9999999999+$x99-$x99+$x9-$x9+$x999-$x999
$x99999999999=$x9999999999





print''?chr$x999%128:$x999
print'$x99'?chr$x99999999%128:$x99999999
$x999=$x999-$x9
$_=$x99999*011unless$x999
$_=$x9*011unless$x99999999999|$x9999999999

Więcej szczegółów

open A,pop; # open the source file
for(@c=<A>){ # read all lines into @c and iterate over them
y/ 9//cd; # remove all but spaces and 9's
s/ +/ /g;s/ $//; # remove duplicate and trailing spaces
$p="((99)+|9+)";$a='+';
s/^ $p$/$1='$2'?ord<>:<>/; # convert input
s/^$p$/print'$2'?chr$1%128:$1/; # convert output
s/^ $p /\$_=$1*011unless/&&y/ /|/; # convert goto
s/ /=/;s/ /$a=-$a/ge; # convert assignment
s!9+!${x.$&}=$&/9;"\$x$&"!eg} # initialize and convert variables
eval$c[$_++]until/-/|$_>@c # run (program counter is in $_)
nutki
źródło