Escreva um intérprete para 99


99

99 (pronunciado "noventa e nove") é uma nova linguagem de programação esotérica (não deve ser confundida com 99 , observe o itálico). Sua tarefa neste desafio é escrever um intérprete para 99 o mais curto possível. O envio com o menor número de bytes vence. O desempatador vai para a submissão postada primeiro.

Como essa pergunta é um pouco mais profunda do que o normal, e estou ansiosa para ver boas respostas, concederei uma recompensa de 250 repetições à minha resposta favorita (não necessariamente a vencedora).

99 Spec

99 é uma linguagem imperativa . Cada linha em um programa 99 é uma única instrução e, durante a execução, o ponteiro da instrução começa na linha superior e passa por cada uma das linhas subsequentes em ordem, executando-as ao longo do caminho. O programa termina quando a última linha é executada. Instruções Goto podem redirecionar o caminho do ponteiro de instruções.

Nova linha, espaço e 9são os únicos três caracteres importantes em um programa 99 . Todos os outros caracteres são completamente ignorados. Além disso, os espaços à direita em cada linha são ignorados e vários espaços em uma linha são lidos como um espaço. ("Nova linha" refere-se a qualquer codificação de quebra de linha comum . Não importa qual o seu intérprete usa.)

Portanto, este programa:

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

É idêntico a este programa:

 9 99 9999
9 9

Variáveis

Todas as variáveis ​​em 99 têm nomes que são um ou mais 9agrupados ( 9+em regex). Por exemplo, 9, 99e 9999999999são todas as variáveis distintas. Naturalmente, existem infinitas (exceto limitações de memória).

O valor de cada variável é um número inteiro de precisão arbitrário assinado . Por padrão, cada variável é atribuída à sua própria representação numérica. Portanto, a menos que tenha sido reatribuída, o valor da variável 9é o número 9 e o valor da variável 99é o número 99 e assim por diante. Você pode pensar nisso como tratar as variáveis ​​como números simples até que sejam explicitamente atribuídas.

Usarei Vpara me referir a um nome de variável arbitrário abaixo.
Cada exemplo de Vpoderia ser substituída com 9, 99, 999, 9999, etc.

Afirmações

Existem cinco tipos diferentes de instruções em 99 . Cada linha em um programa 99 contém exatamente uma instrução.

A sintaxe descrita aqui assume que todos os caracteres estranhos foram removidos, todos os espaços finais foram removidos e todas as seqüências de vários espaços foram substituídas por espaços únicos.

1. Nenhuma operação


Uma linha vazia é um no-op . Não faz nada (além de aumentar o ponteiro da instrução).

2. Saída

V

Uma única variável Vem uma linha imprime essa variável em stdout.

Se Vtem um número ímpar de 9'S ( 9, 999, etc), então o valor de número inteiro Vdividido por 9 será impresso (em valores decimais).

Se Vtem um número par de 9's ( 99, 9999, etc), então o ASCII caracteres com código Vdividido por 9, mod 128 vai ser impressa. (Ou seja (V / 9) % 128, um valor de 0 a 127.)

Exemplo : O programa

9
9999

iria imprimir 1W. A primeira linha é impressa 1porque 9/9 é 1. A segunda linha é impressa Wporque 9999/9 é 1111, e 1111 mod 128 é 87 e 87 é o código de caractere W.

Observe que as quebras de linha não são impressas entre os tokens de saída. \nprecisa ser explicitamente impresso para uma quebra de linha.

3. Entrada

 V

Uma única variável Vem uma linha com um espaço à esquerda pega a entrada do stdin e a armazena nessa variável.

Se Vtiver um número ímpar de 9, o usuário poderá digitar qualquer número inteiro assinado e Vserá definido como 9 vezes esse valor.

Se Vtiver um número par de 9, o usuário poderá digitar qualquer caractere ASCII e Vserá definido como 9 vezes seu código de caractere.

Exemplo : dado -57e Acomo entrada, este programa

 9
9
 99
99

iria produzir -57A. Internamente, a variável 9teria o valor -513 e 99o valor 585.

Seu intérprete pode assumir que as entradas são sempre sintaticamente válidas.

4. Cessão

Essa declaração pode ser arbitrariamente longa. São duas ou mais variáveis ​​em uma linha, separadas por espaços:

V1 V2 V3 V4 V5 ...

Isso atribui à soma de todos os 's com índices pares, menos a soma dos ' s com índices ímpares (excluindo ). As atribuições são por valor, não por referência.V1VVV1

Pode ser traduzido na maioria dos idiomas como .V1 = V2 - V3 + V4 - V5 + ...

Portanto, se houver apenas duas variáveis, é uma atribuição normal:

V1 V2V1 = V2

Se houver três, é subtração:

V1 V2 V3V1 = V2 - V3

E o sinal +/ -continua alternando com cada variável adicional:

V1 V2 V3 V4V1 = V2 - V3 + V4

Exemplo : Este programa produziria 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. Saltar (pular se tudo zero)

Esta declaração também pode ser arbitrariamente longa. São duas ou mais variáveis ​​em uma linha, separadas por espaços, com um espaço à esquerda :

 V1 V2 V3 V4 V5 ...

Se alguns dos valores forem diferentes de zero, isso se comportará como um no-op. O ponteiro de instruções é movido para a próxima linha, como de costume.V1

Se todos os valores forem zero, o ponteiro da instrução será movido para o número da linha . As linhas são indexadas a zero, portanto, se for zero, o ponteiro se moverá para a linha superior. O programa termina (normalmente, sem erro) se for negativo ou for maior que o índice mais alto possível (número de linhas menos um).V1 V1V1V1

Observe que não foi dividido por 9 aqui. E como é impossível ter uma variável com um valor que não seja múltiplo de 9, apenas os números de linha com múltiplos de 9 podem ser saltados.V1

Exemplos:

Este programa imprimirá 1para sempre:

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.

Este programa

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

produzirá os números 11 para 1, em ordem decrescente, cercados por G's:

G11G10G9G8G7G6G5G4G3G2G1G

detalhes adicionais

O intérprete ideal será executado na linha de comando com o nome do arquivo do programa 99 como argumento. A E / S também será feita em tempo real na linha de comando.

Você pode, no entanto, apenas escrever uma função de intérprete que considere o programa como uma string, bem como uma lista dos tokens de entrada (por exemplo ["-57", "A"]). A função deve imprimir ou retornar a sequência de saída.

Maneiras ligeiramente diferentes de executar o intérprete e manipular E / S são boas se essas opções forem impossíveis no seu idioma.


Bônus: escreva algo bacana em 99 e vou colocá-lo com prazer neste post como um exemplo.


Espero que tenham gostado do meu 99º desafio! : D


9
Eu considerei votar, mas sua pontuação atual é 9…
wchargin

30
@WChargin parece agora você vai ter que tentar obtê-lo 99.
trlkly

5
Certamente há um bônus para a auto-hospedagem (escrevendo um 99 intérprete em 99 ), não?
Gabe

5
@ Gabe Uma resposta como essa provavelmente receberia a recompensa, mas se essa fosse a única resposta, o que interpretaria o intérprete? ;)
Hobbies de Calvin

1
@Optimizer funciona: pastebin.com/raw.php?i=h73q58FN
coredump

Respostas:


16

CJam, 157 bytes

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

Experimente online:

Explicação

Tentar formatar isso com indentação e comentários adequados provavelmente levaria uma eternidade, por isso darei um resumo algorítmico.

O código é um bloco, analógico do CJam para funções anônimas. O bloco espera a sequência do programa e a lista de entradas na pilha quando executada.

A inicialização consiste em três etapas. Primeiro, a lista de entrada é salva. Em seguida, todos os caracteres do programa que não são significativos são removidos e o resultado é dividido em uma lista de linhas e salvos. Finalmente, a lista de variáveis ​​é inicializada. Essa lista mapeia cada variável, indexada pelo tamanho do nome, para seu valor dividido por 9 (uma variável nunca pode conter um valor que não seja múltiplo de 9, e todas as operações, exceto as que se beneficiam dessa alteração). A lista é inicializada até o comprimento da linha mais longa, que é um limite superior no nome de vara mais longo presente. Também há um pouco de inicialização implícita devido aos valores iniciais da variável: o número da linha é 0 e o índice de entrada é -1.

O intérprete é implementado como seria de esperar: um loop que lê a próxima linha, incrementa o número da linha e executa a linha enquanto o número da linha aponta para uma linha existente. A análise de linha primeiro verifica se a linha não está vazia, depois se ramifica com base na aridade 1 ou> 1, depois se ramifica com base na existência de um espaço à esquerda. Esses quatro ramos imitam as quatro operações (excluindo não-op) de uma maneira mais direta, ainda que de forma agressiva, como todo o resto. Talvez uma otimização da observação seja que, como uma sequência de entrada válida sempre deve produzir um elemento do tipo esperado pelo programa, eu omiti a criação de casos separados para entrada com base no comprimento do nome da variável. Supõe-se simplesmente que o elemento lido na lista de entrada seja do tipo esperado.


15
+1. Muito curto. Agora, você pode escrever um intérprete CJam em 99 ? ;-)
coredump

9
@coredump * estremece *
Runer112

Droga, eu só consigo 195 e perdi a esperança e desisti: P
Optimizer

Isso não requer o módulo correto ao imprimir valores negativos. Isso pode ser corrigido substituindo 128%por 128,=.
Martin Ender

26

Python 3, 421 414 410 404 388 395 401 bytes

Golfe:

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

Praticamente apenas uma implementação literal da especificação, diminuiu o máximo possível.

Execute a partir da linha de comando, fornecendo um arquivo de código-fonte 99 como o único argumento (por exemplo, o último exemplo do OP):

> python3 ninetynine.py countdown.txt
G11G10G9G8G7G6G5G4G3G2G1G
>

Como um bônus adicional, aqui está uma implementação (bastante ruim) de "99 garrafas" em 99 : http://pastebin.com/nczmzkFs


1
@DLosc: sobre o seu primeiro ponto: eu também pensei que elsedepois que um número pudesse ser removido, mas quando tentei antes, recebi um erro de sintaxe. Suas outras dicas são muito apreciadas!
Mac

3
@ coreoredump: a maneira como a especificação é escrita, cada variável sempre terá um valor que é divisível por nove. Achei mais conciso permitir que as variáveis ​​assumissem qualquer valor e apenas multiplicar / dividir por nove, conforme necessário (em particular, na gotorotina e ao obter o valor padrão da variável). Para um usuário do idioma, isso não faz diferença.
Mac

2
Não o elsepróprio, apenas o espaço à sua frente. Por exemplo 3*n+1if n%2else n//2.
DLosc

1
@DLosc: desculpe, eu errei - eu realmente quis dizer o espaço, não o else. Por exemplo, eu tentei substituir print(w if L(e)%2 else chr(w%128))com print(w if L(e)%2else chr(w%128))e tem uma exceção de sintaxe.
Mac

1
Estranho - eu testei no ideone.com e funcionou, mas você está certo, ele não funciona no interpretador Python3 (3.4.0 no Ubuntu). Esta publicação de dicas esclarece: um número seguido por um token alfabético funciona em geral, mas não para tokens iniciados com eor E, e (pelos comentários) não para os 0ordois.
DLosc

16

Lisp comum, 1180 857 837 836 bytes

Eu sei que isso não vai ganhar, mas eu me diverti jogando golfe. Consegui remover 343 bytes, que são mais de dois 99 intérpretes escritos em CJam.

Além disso, de maneira bastante divertida, quanto mais eu tento compactá-lo, mais me convenceu de que, para o Common Lisp, é mais curto compilar o código do que tentar interpretá-lo em tempo real.

(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)>))))
  • A análise lexical e a geração de código são intercaladas: não guardo a representação interna, mas processo diretamente cada linha.
  • existe um único tagbodypara executar 2 loops:

     (... (tagbody % ... (go %) $ ... (go $)) result)
    
  • variáveis ​​locais são declaradas em &aux

  • não gera um fechamento, mas diretamente o código interpretado
  • etc.

Ungolfed, comentou

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

Usamos entrada / saída padrão durante a avaliação, ou seja, usamos padrão reade princfunções. Portanto, o código resultante pode ser executado na linha de comando, conforme mostrado abaixo.

As entradas não são completamente bem higienizadas ao executar 99 programas: supõe-se que o usuário saiba que tipo de valores são esperados.

A única sobrecarga de tempo de execução possível pode ocorrer ao pular, pois precisamos avaliar o valor de uma variável e corresponder esse valor a um rótulo. Exceto que, o intérprete deve ser bastante eficiente.

Com base na inteligente observação do Mac de que não precisamos dividir e multiplicar por 9 a cada vez, a versão atual consegue nunca se dividir nem multiplicar por 9 durante a execução.

Exemplo

Se substituirmos defmacropor defun, veremos o código gerado. Por exemplo:

(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

")

Aqui está o código resultante:

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

Quando executado, imprime "G11G10G9G8G7G6G5G4G3G2G1G"

Linha de comando

Podemos construir um executável descartando um núcleo e especificando a toplevelfunção. Defina um arquivo chamado boot.lisponde você coloca o defmacroe, em seguida, escreva o seguinte:

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

A execução sbcl --load boot.lispfornece a seguinte saída:

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

Em seguida, executando o programa 99 compilado :

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

99 garrafas

Se você estiver interessado, aqui está o código compilado para o programa de 99 garrafas, escrito na resposta do Mac : http://pastebin.com/ZXe839CZ (esta é a versão antiga em que temos jmpe endrotula, uma lambda circundante e aritmética mais bonita).

Aqui está uma execução com a nova versão, para provar que ainda funciona: http://pastebin.com/raw.php?i=h73q58FN


6

TI-84 Básica (Script da Calculadora), 376 373 377 381 bytes

Se for executado em uma calculadora TI-84, você poderá usá-lo em um teste padronizado ... por isso é útil;)

Versão mínima do sistema operacional - 2.53MP (MathPrint) devido ao sigma da soma

#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

As diretrizes PS ASCII não puderam ser seguidas exatamente, mas no TI-Basic :há uma nova linha. Portanto, todas as novas linhas reais no código significam que o :ou #no início de cada linha não é necessário. Os tokens iniciais :e #apenas diferenciam entre comentários e código.

Despejo hexagonal original (376 bytes)

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

Editar nº 1 - Otimizado em 3 bytes usando a observação do Mac. Edições nº 2 e nº 3 - Correção de erros detectados pelo Runer112.


11
Ser fácil de usar em situações estressantes, como testes padronizados, é exatamente o que eu projetei para a 99 .
Calvin's Hobbies

1
Posso sugerir o uso de um caractere diferente, como #, para os comentários? (NB: Os comentários no código real são implementados como uma linha com apenas uma string não fechada, que clobbers Ans)
Riking

8
Você já tentou executar isso? Eu não tenho, mas só de olhar um pouco mais, vi o que parecem ser pelo menos meia dúzia de bugs. Por exemplo: variáveis ​​não são inicializadas com seus valores, a Ansentrada é sobrescrita; assim, Ans->Str0na linha 6 ocorrerá um erro, há várias instâncias em que o argumento de comprimento de um sub()comando pode ser zero, o que resulta em um erro, Ansna linha 11 será uma string assim Ans-Jserá erro ... E eu só olhei para a primeira metade do programa.
Runer112

1
@ Timtech Isso ainda deixa os outros problemas, no entanto. Como mencionei, há a falta de E / S de caracteres, falta de inicialização de variável e várias instâncias em que um sub()comando pode ter comprimento zero e gerar um erro. E uma vez que as sub()invocações são corrigidas, receio que possam revelar mais problemas.
Runer112

1
A Timtech estava me referindo a isso: "Por padrão, cada variável é atribuída a sua própria representação numérica. Portanto, a menos que tenha sido reatribuída, o valor da variável 9é o número 9 e o valor da variável 99é o número 99, e assim por diante." E cadeias de comprimento 0 podem ser produzidas por meios como "", mas é uma espécie de bug que basicamente nenhum comando de manipulação de cadeia pode consumir ou produzir uma cadeia vazia, inclusive sub().
Runer112

5

C 426 458 481 497

Edit Talvez eu esteja indo longe demais, mas isso funciona com o Visual C: removeu stdio.h, usando int em vez de FILE * para fopen e getc

Editar 2 Reordene a etapa de execução, mais confusão, 32 caracteres salvos

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

Programa de console independente, nome do programa obtido na linha de comando e entrada / saída via console.

Estilo antigo K&R, tipo padrão int para variáveis ​​e parâmetros globais. Supondo que o EOF seja definido como -1 (como em todas as implementações de C que conheço)

Compila com avisos com o Visual Studio 2010 (projeto C ++ do console do Win32, compila como C) Compila no Ideone, mas não pode ser executado porque precisa de um arquivo.

Primeiro passo, o código fonte é lido e analisado, cada linha é armazenada como uma sequência de números inteiros com base nos números de 9s. Se houver um espaço em branco à esquerda, o primeiro número é negativo. Então: 9 BLAH 99 9a9bb9c9( 9 99 9999) torna-se -1,2,4 Existe um atalho - não é tão legal: todos os códigos ascii menores que '' são considerados novas linhas.

Nesta etapa, todas as variáveis ​​usadas são pré-inicializadas.

A etapa de execução segue as especificações, sem frescuras, exceto os números de armazenamento divididos por 9.

Mais legível mesmo código (espero), espaços e novas linhas adicionadas

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
Também funciona com o GCC 4.8.2. Compila como C99!
EMBLEM

4

Haskell, 550 bytes

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

Exemplo de execução com o programa "countdown" armazenado no arquivo i.99

$ ./99 i.99
G11G10G9G8G7G6G5G4G3G2G1G

Versão não destruída:

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

Uma função com 2 parâmetros

  • código de programa como uma cadeia de linhas múltiplas
  • entrada como uma matriz

O terceiro parâmetro opcional (padrão 10k) é o número máximo de iterações - não gosto de um programa que é executado para sempre

JSFiddle Para testar

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

q / 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

O script é uma mistura de q e k; portanto, primeiro defino algumas palavras-chave q que quero usar várias vezes nas funções k. (basicamente #define macros)

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

f lê o arquivo passado para o programa e remove caracteres desnecessários

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 pega uma lista / vetor e multiplica os índices ímpares por -1

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

b é apenas uma função vazia, usada para as linhas sem operação

p é a função de impressão.

Ké uma função que examina uma variável. Se a variável existir, ela retornará, caso contrário, apenas retornará o literal.

//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 é a função de atribuição.

g é a função goto.

r pega uma string e decide qual operação deve ser aplicada.

E finalmente, eu apenas percorro a flista de strings, com no iterador. A função goto será atualizada nconforme necessário.


3

Perl, 273 266 255 244 238

Quebras de linha adicionadas para maior clareza.

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

Nome do programa usado na linha de comando:

$ perl 99.pl 99beers.99

Cada linha do programa é convertida em código Perl, por exemplo:

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

Mais detalhes

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 $_)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.