Написать переводчика для 99


99

99 (произносится как «девяносто девять») - это совершенно новый язык эзотерического программирования (не путать с 99 , обратите внимание на курсив). Ваша задача в этой задаче - написать переводчика для 99, который будет максимально коротким. Представление с наименьшим количеством байтов выигрывает. Tiebreaker переходит к представлению, опубликованному первым.

Поскольку этот вопрос немного глубже, чем обычно, и мне не терпится увидеть хорошие ответы, я буду награждать награду в 250 представителей своим любимым ответом (не обязательно победителем).

99 Спец

99 - императивный язык. Каждая строка в программе 99 представляет собой один оператор , и во время выполнения указатель инструкции начинается с верхней строки и проходит по порядку через каждую из последующих строк, выполняя их по пути. Программа заканчивается, когда последняя строка была выполнена. Операторы Goto могут перенаправлять путь указателя инструкции.

Новая строка, пробел и 9единственные три символа, которые имеют значение в программе 99 . Все остальные персонажи полностью игнорируются. Кроме того, завершающие пробелы в каждой строке игнорируются, и несколько пробелов в строке читаются как один пробел. («Новая строка » относится к любой обычной кодировке разрыва строки . Не имеет значения, какой из них использует ваш интерпретатор.)

Итак, эта программа:

   9      BLAH        99   9a9bb9c9
9 this line and the next have 6 trailing spaces 9      
      

Идентичен этой программе:

 9 99 9999
9 9

переменные

Все переменные в 99 имеют имена, которые связаны одним или несколькими 9( 9+в регулярном выражении). Так , например, 9, 99и 9999999999все различные переменные. Естественно, их бесконечно много (за исключением ограничений памяти).

Значением каждой переменной является целое число произвольной точности со знаком. По умолчанию каждой переменной присваивается собственное числовое представление. Поэтому, если он не был переназначен, значением переменной 9является число 9, а значением переменной 99является число 99 и так далее. Вы можете думать об этом как об обработке переменных как простых чисел, пока они не будут явно назначены.

Я буду использовать Vдля ссылки на произвольное имя переменной ниже.
Каждый экземпляр Vможет быть заменен 9, 99, 999, 9999и т.д.

Заявления

В 99 есть пять различных типов операторов . Каждая строка в программе 99 содержит ровно одно утверждение.

Синтаксис, описанный здесь, предполагает, что все посторонние символы были удалены, все конечные пробелы были удалены, и все последовательности из нескольких пробелов были заменены одиночными пробелами.

1. Нет операции


Пустая строка не является опцией . Это ничего не делает (кроме увеличения указателя инструкции).

2. Выход

V

Одна переменная Vв строке выводит эту переменную в стандартный вывод.

Если Vимеет нечетное число 9'( 9, 999и т. Д.), То Vбудет напечатано целое число, деленное на 9 (в десятичном виде).

Если Vимеет четное число 9s ( 99, 9999и т. Д.) , То будет напечатан символ ASCII с кодом, Vразделенным на 9, mod 128. (Это (V / 9) % 128значение от 0 до 127.)

Пример : программа

9
9999

будет печатать 1W. Первая строка печатается, 1потому что 9/9 - 1. Вторая строка печатается, Wпотому что 9999/9 - 1111, а 1111 mod 128 - 87, а 87 - код символа для W.

Обратите внимание, что разрывы строк не выводятся между токенами вывода. \nдолжен быть явно напечатан для разрыва строки.

3. Ввод

 V

Одна переменная Vв строке с начальным пробелом принимает входные данные из stdin и сохраняет их в этой переменной.

Если Vимеет нечетное число 9's, то пользователь может ввести любое целое число со знаком, и Vбудет установлено в 9 раз больше этого значения.

Если Vимеет четное число 9«s», то пользователь может ввести любой символ ASCII, и для него Vбудет установлено значение, в 9 раз превышающее его код символа.

Пример : данный -57и Aкак вход, эта программа

 9
9
 99
99

будет выводить -57A. Внутри переменная 9будет иметь значение -513 и 99585.

Ваш переводчик может предположить, что входные данные всегда синтаксически действительны.

4. Назначение

Это утверждение может быть сколь угодно длинным. Это две или более переменных в строке, разделенных пробелами:

V1 V2 V3 V4 V5 ...

Это присваивает сумму всех с четными индексами минус сумма с нечетными индексами (исключая ). Назначения по значению, а не по ссылке.V1VVV1

Это может быть переведено на большинство языков как .V1 = V2 - V3 + V4 - V5 + ...

Итак, если есть только две переменные, это нормальное присваивание:

V1 V2V1 = V2

Если их три, то это вычитание:

V1 V2 V3V1 = V2 - V3

И знак +/ -продолжает переключаться с каждой дополнительной переменной:

V1 V2 V3 V4V1 = V2 - V3 + V4

Пример : эта программа выдаст 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. Перейти (прыгать, если все ноль)

Это утверждение также может быть сколь угодно длинным. Это две или более переменных в строке, разделенных пробелами, с начальным пробелом :

 V1 V2 V3 V4 V5 ...

Если некоторые из значений, кроме того , ненулевые, то это ведет себя так же, как отсутствие операции. Указатель инструкции перемещается на следующую строку как обычно.V1

Если все значения, кроме того, равны нулю, указатель инструкции перемещается на номер строки . Строки индексируются нулем, поэтому, если ноль, указатель перемещается на верхнюю строку. Программа завершается (обычно без ошибок), если она отрицательна или превышает максимально возможный индекс (количество строк минус одна).V1 V1V1V1

Обратите внимание, что здесь не было разделено на 9. И поскольку невозможно, чтобы переменная была значением, не кратным 9, можно перейти только к номерам строк, кратным 9.V1

Примеры:

Эта программа напечатает 1навсегда:

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.

Эта программа

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

выведет числа от 11 до 1 в порядке убывания, окруженные Gсимволами:

G11G10G9G8G7G6G5G4G3G2G1G

дополнительные детали

Идеальный интерпретатор будет запускаться из командной строки с именем файла программы 99 в качестве аргумента. Ввод / вывод также будет выполняться на лету в командной строке.

Вы можете, однако, просто написать функцию интерпретатора, которая принимает в программу строку, а также список входных токенов (например ["-57", "A"]). Функция должна напечатать или вернуть выходную строку.

Немного разные способы запуска интерпретатора и обработки ввода / вывода хороши, если эти опции невозможны на вашем языке.


Бонус: Напишите что-нибудь классное в 99, и я с удовольствием выложу это в качестве примера.


Надеюсь, вам понравился мой 99-й вызов! : D


9
Я рассмотрел голосование против, но ваш текущий счет 9 ...
wchargin

30
Похоже, @WChargin теперь вам придется попробовать получить 99.
Trlkly

5
Конечно, есть бонус за самостоятельное размещение (написание 99 переводчика в 99 ), нет?
Гейб

5
@Gabe Подобный ответ, вероятно, получит награду, но если бы это был единственный ответ, что интерпретировал бы переводчик? ;)
Увлечения Кэлвина

1
@ Оптимизатор работает: pastebin.com/raw.php?i=h73q58FN
coredump

Ответы:


16

CJam, 157 байтов

{: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}

Попробуйте онлайн:

объяснение

Попытка отформатировать это с правильными отступами и комментариями, вероятно, займет вечность, поэтому я просто дам алгоритмическое резюме.

Код представляет собой блок, аналог CJam для анонимных функций. Блок ожидает строку программы и список входных данных в стеке при выполнении.

Инициализация состоит из трех этапов. Во-первых, список ввода сохраняется. Затем каждый символ в программе, который не имеет смысла, удаляется, а результат разбивается на список строк и сохраняется. Наконец, список переменных инициализируется. Этот список отображает каждую переменную, проиндексированную по длине имени, на ее значение, деленное на 9 (переменная никогда не может содержать значение, не кратное 9, и все операции, кроме goto, извлекают выгоду из этого изменения). Список инициализируется до длины самой длинной строки, которая является верхней границей самого длинного доступного имени. Есть также небольшая неявная инициализация из-за начальных значений переменных: номер строки равен 0, а входной индекс равен -1.

Интерпретатор реализован так, как и следовало ожидать: цикл, который читает следующую строку, увеличивает номер строки и выполняет строку, в то время как номер строки указывает на существующую строку. Разбор строки сначала проверяет, что строка не пустая, затем ветвится в зависимости от того, является ли арность 1 или> 1, затем ветвится в зависимости от того, был ли начальный пробел. Эти четыре ветви эмулируют четыре (исключая неоперационные) операции в основном простым способом, хотя агрессивно играют в гольф, как и все остальное. Возможно, одна оптимизация примечания состоит в том, что, поскольку допустимая входная последовательность всегда должна создавать элемент типа, ожидаемого программой, я исключил создание отдельных случаев для ввода на основе длины имени переменной. Просто предполагается, что элемент, считываемый из списка ввода, имеет ожидаемый тип.


15
+1. Довольно короткий. Теперь, вы можете написать интерпретатор CJam в 99 ? ;-)
coredump

9
@coredump * вздрагивает *
Runer112

Блин, я могу получить только 195, а потом потерял надежду и сдался: P
Оптимизатор

Это не принимает правильное значение по модулю при печати отрицательных значений. Это можно исправить, заменив 128%на 128,=.
Мартин Эндер

26

Python 3, 421 414 410 404 388 395 401 байт

Golfed:

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='')

Ungolfed:

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='')

Практически просто буквальная реализация спецификации, насколько я могу понять.

Запустите из командной строки, указав в качестве единственного аргумента файл с исходным кодом 99 (например, последний пример из OP):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

В качестве дополнительного бонуса, вот (довольно плохая) реализация «99 бутылок» в 99 : http://pastebin.com/nczmzkFs


1
@DLosc: относительно вашего первого пункта: я тоже думал, что elseпосле числа можно удалить, но когда я попробовал это раньше, я получил синтаксическую ошибку. Ваши другие советы очень ценятся!
Mac

3
@coredump: при написании спецификации каждая переменная всегда будет иметь значение, которое делится на девять. Я нашел более кратким, чтобы позволить переменным принимать любое значение и только умножать / делить на девять по мере необходимости (в частности, в gotoпроцедуре и при получении значения переменной по умолчанию). Что касается пользователя языка, это не имеет значения.
Mac

2
Не elseсам по себе, только пространство перед ним. Например 3*n+1if n%2else n//2.
DLosc

1
@DLosc: извините, я ошибся - я действительно имел в виду пространство, а не else. Например, я попытался заменить print(w if L(e)%2 else chr(w%128))на print(w if L(e)%2else chr(w%128))и получил синтаксическое исключение.
Mac

1
Странно - я тестировал на ideone.com и он работал, но вы правы, он не работает в реальном интерпретаторе Python3 (3.4.0 в Ubuntu). В этом посте с подсказками разъясняется: число, за которым следует алфавитный токен, работает в целом, но не для токенов, начинающихся с eили E, и (из комментариев) не для 0orни того, ни другого.
DLosc

16

Common Lisp, 1180 857 +837 836 байт

Я знаю, что это не победит, но я получил удовольствие от игры в гольф. Мне удалось удалить 343 байта, что составляет более двух 99 интерпретаторов, написанных на CJam.

Кроме того, довольно забавно, что чем больше я пытаюсь сжать его, тем больше меня убеждают, что для Common Lisp компилировать код короче, чем пытаться интерпретировать его на лету.

(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)>))))
  • лексический анализ и генерация кода чередуются: я не храню внутреннее представление, а обрабатываю непосредственно каждую строку.
  • есть один, tagbodyчтобы выполнить 2 цикла:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • локальные переменные объявлены в &aux

  • не генерировать замыкание, а непосредственно интерпретируемый код
  • и т.п.

Ungolfed, прокомментировал

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

Мы используем стандартный ввод / вывод во время оценки, то есть мы используем стандарт readи princфункции. Следовательно, полученный код можно сделать исполняемым в командной строке, как показано ниже.

При запуске 99 программ входы не полностью очищены : предполагается, что пользователь знает, какие значения ожидаются.

Единственные возможные издержки времени выполнения могут возникнуть при переходе, так как мы должны оценить значение переменной и сопоставить это значение с меткой. Кроме того, переводчик должен быть достаточно эффективным.

Основываясь на умном наблюдении от Mac, что нам не нужно делить и умножать на 9 каждый раз, текущей версии удается никогда не делить и не умножать на 9 во время выполнения.

пример

Если мы заменим defmacroна defun, мы увидим сгенерированный код. Например:

(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

")

Вот результирующий код:

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

После выполнения выдает «G11G10G9G8G7G6G5G4G3G2G1G»

Командная строка

Мы можем создать исполняемый файл, выгрузив ядро ​​и указав toplevelфункцию. Определите файл с именем, boot.lispкуда вы положили defmacro, и затем напишите следующее:

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

Запуск sbcl --load boot.lispдает следующий вывод:

$ 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]

Затем запустите скомпилированную программу 99 :

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

99 бутылок

Если вам интересно, вот скомпилированный код для программы на 99 бутылок, написанный в ответе Mac : http://pastebin.com/ZXe839CZ (это старая версия, где у нас есть jmpи endметки, и окружающая лямбда и более симпатичная арифметика).

Вот исполнение с новой версией, чтобы доказать, что оно все еще работает: http://pastebin.com/raw.php?i=h73q58FN


6

TI-84 Basic (скрипт калькулятора), 376 373 377 381 байт

Если он работает на калькуляторе TI-84, вы сможете использовать его в стандартизированном тесте ... так что это полезно;)

Минимальная версия операционной системы - 2,53MP (MathPrint) из-за сигма суммирования

#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

PS ASCII руководящие принципы не могли быть точно выполнены, но в TI-Basic :это новая строка. Таким образом, все действительные символы новой строки в коде означают, что :или #в начале каждой строки не требуется. Начало токенов :и #просто различие между комментариями и кодом.

Оригинальный шестнадцатеричный дамп (376 байт)

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

Редактирование # 1 - Оптимизировано 3 байта с использованием наблюдения Mac. Редактирование # 2 и # 3 - Исправлены ошибки, обнаруженные Runer112.


11
Простота использования в стрессовых ситуациях, таких как стандартизированные тесты, - это как раз то , для чего я разработал 99 .
Увлечения Кэлвина

1
Могу ли я предложить использовать другой символ, например #, для комментариев? (Примечание: комментарии в реальном коде реализованы в виде строки с только незамкнутой строкой, которая забивает ответ)
Riking

8
Вы на самом деле пытались запустить это? Я не заметил, но, просто взглянув на это немного больше, я обнаружил, по-видимому, как минимум полдюжины ошибок. Например: переменные не инициализируются своими значениями, Ansввод перезаписывается, поэтому Ans->Str0в строке 6 произойдет ошибка, есть несколько случаев, когда аргумент длины sub()команды может быть нулевым, что приводит к ошибке, Ansв строке 11 будет строка так Ans-Jбудет и ошибка ... И я только посмотрел примерно на первую половину программы.
Runer112

1
@Timtech Это все еще оставляет другие проблемы, хотя. Как я уже упоминал, отсутствует символьный ввод-вывод, отсутствует инициализация переменных и многократные случаи, когда sub()команда может иметь нулевую длину и выдавать ошибку. И как только sub()вызовы исправлены, я боюсь, что это может выявить больше проблем.
Runer112

1
@Timtech Я имел в виду следующее: «По умолчанию каждая переменная присваивается своему собственному числовому представлению. Поэтому, если она не была переназначена, значением переменной 9является число 9, а значением переменной 99является число 99, и так далее." И строки длины 0 могут быть получены с помощью подобных средств "", но это своего рода ошибка, которая, по сути, ни одна команда манипуляции со строками не может использовать или производить пустую строку, в том числе sub().
Runer112

5

С 426 458 481 497

Edit Возможно, я захожу слишком далеко, но это работает с Visual C: удален stdio.h, с использованием int вместо FILE * для fopen и getc

Изменить 2 Изменить порядок выполнения шага, больше беспорядка, 32 символа сохранены

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);
}

Автономная консольная программа, имя программы берется из командной строки и ввод / вывод через консоль.

Старый стиль K & R, тип по умолчанию int для глобальных переменных и параметров. Предполагая, что EOF определен как -1 (как и в каждой реализации C, о которой я знаю)

Компилируется с предупреждениями с помощью Visual Studio 2010 (консольный проект C32 для Win32, компилируется как C). Компилируется в Ideone, но не может работать, так как ему нужен файл.

Первый шаг, исходный код читается и анализируется, каждая строка сохраняется в виде последовательности целых чисел на основе чисел 9. Если есть пробел, первая цифра отрицательна. Итак: 9 BLAH 99 9a9bb9c9( 9 99 9999) становится. -1,2,4 Есть ярлык - не такой законный: все коды ascii, меньшие чем ', считаются символами новой строки.

На этом этапе все используемые переменные предварительно инициализируются.

Шаг выполнения соответствует спецификациям, без излишеств, за исключением сохраненных номеров, разделенных на 9.

Более читаемый код (надеюсь), добавлены пробелы и переводы строк

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);
}

1
Также работает с GCC 4.8.2. Компилируется как C99!
ЭМБЛЕМА

4

Haskell, 550 байт

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

Пример запуска с программой «обратный отсчет», сохраненной в файле i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Безголовая версия:

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

4

JavaScript (ES6) 340 352

Функция с 2 параметрами

  • программный код в виде многострочной строки
  • ввод в виде массива

Третий необязательный параметр (по умолчанию 10k) - это максимальное количество итераций - мне не нравится программа, которая работает вечно

JSFiddle для тестирования

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

4

к / к, 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

Скрипт представляет собой смесь q и k, поэтому сначала я определю несколько ключевых слов q, которые я хочу использовать несколько раз в k функциях. (в основном #define макросы)

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

f читает переданный в программу файл и удаляет ненужные символы

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 берет список / вектор и умножает нечетные индексы на -1

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

b это просто пустая функция, используемая для неоперативных линий

p это функция печати.

Kэто функция, которая проверяет переменную. Если переменная существует, она возвращает ее, в противном случае она просто возвращает литерал.

//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 это функция присваивания.

g это функция перехода.

r берет строку и решает, какую операцию нужно применить.

И, наконец, я просто перебираю fсписок строк, используя nв качестве итератора. Функция goto будет обновляться по nмере необходимости.


3

Perl, 273 266 255 244 238

Добавлены разрывы строк для ясности.

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

Имя программы берется из командной строки:

$ perl 99.pl 99beers.99

Каждая строка программы конвертируется в код Perl, например:

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

Подробнее

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 $_)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.