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 9
sã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 9
agrupados ( 9+
em regex). Por exemplo, 9
, 99
e 9999999999
sã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 V
para me referir a um nome de variável arbitrário abaixo.
Cada exemplo de V
poderia 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 V
em uma linha imprime essa variável em stdout.
Se V
tem um número ímpar de 9
'S ( 9
, 999
, etc), então o valor de número inteiro V
dividido por 9 será impresso (em valores decimais).
Se V
tem um número par de 9
's ( 99
, 9999
, etc), então o ASCII caracteres com código V
dividido 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 1
porque 9/9 é 1. A segunda linha é impressa W
porque 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. \n
precisa ser explicitamente impresso para uma quebra de linha.
3. Entrada
V
Uma única variável V
em uma linha com um espaço à esquerda pega a entrada do stdin e a armazena nessa variável.
Se V
tiver um número ímpar de 9
, o usuário poderá digitar qualquer número inteiro assinado e V
será definido como 9 vezes esse valor.
Se V
tiver um número par de 9
, o usuário poderá digitar qualquer caractere ASCII e V
será definido como 9 vezes seu código de caractere.
Exemplo : dado -57
e A
como entrada, este programa
9
9
99
99
iria produzir -57A
. Internamente, a variável 9
teria o valor -513 e 99
o 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.V1
V
V
V1
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 V2
→ V1 = V2
Se houver três, é subtração:
V1 V2 V3
→ V1 = V2 - V3
E o sinal +
/ -
continua alternando com cada variável adicional:
V1 V2 V3 V4
→ V1 = 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
V1
V1
V1
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á 1
para 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.
- Aqui está um Pastebin de um puro programa "99 garrafas de cerveja" da resposta do Mac .
fonte
Respostas:
CJam, 157 bytes
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.
fonte
128%
por128,=
.Python 3,
421414410404388395401 bytesGolfe:
Ungolfed:
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):
Como um bônus adicional, aqui está uma implementação (bastante ruim) de "99 garrafas" em 99 : http://pastebin.com/nczmzkFs
fonte
else
depois que um número pudesse ser removido, mas quando tentei antes, recebi um erro de sintaxe. Suas outras dicas são muito apreciadas!goto
rotina e ao obter o valor padrão da variável). Para um usuário do idioma, isso não faz diferença.else
próprio, apenas o espaço à sua frente. Por exemplo3*n+1if n%2else n//2
.else
. Por exemplo, eu tentei substituirprint(w if L(e)%2 else chr(w%128))
comprint(w if L(e)%2else chr(w%128))
e tem uma exceção de sintaxe.e
orE
, e (pelos comentários) não para os0or
dois.Lisp comum,
1180857837836 bytesEu 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.
existe um único
tagbody
para executar 2 loops:variáveis locais são declaradas em
&aux
Ungolfed, comentou
Usamos entrada / saída padrão durante a avaliação, ou seja, usamos padrão
read
eprinc
funçõ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
defmacro
pordefun
, veremos o código gerado. Por exemplo:Aqui está o código resultante:
Quando executado, imprime "G11G10G9G8G7G6G5G4G3G2G1G"
Linha de comando
Podemos construir um executável descartando um núcleo e especificando a
toplevel
função. Defina um arquivo chamadoboot.lisp
onde você coloca odefmacro
e, em seguida, escreva o seguinte:A execução
sbcl --load boot.lisp
fornece a seguinte saída:Em seguida, executando o programa 99 compilado :
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
jmp
eend
rotula, 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
fonte
TI-84 Básica (Script da Calculadora),
376373377381 bytesSe 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
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)
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.
fonte
#
, 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)Ans
entrada é sobrescrita; assim,Ans->Str0
na linha 6 ocorrerá um erro, há várias instâncias em que o argumento de comprimento de umsub()
comando pode ser zero, o que resulta em um erro,Ans
na linha 11 será uma string assimAns-J
será erro ... E eu só olhei para a primeira metade do programa.sub()
comando pode ter comprimento zero e gerar um erro. E uma vez que assub()
invocações são corrigidas, receio que possam revelar mais problemas.9
é o número 9 e o valor da variável99
é 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, inclusivesub()
.C 426
458 481 497Edit 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
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
fonte
Haskell, 550 bytes
Exemplo de execução com o programa "countdown" armazenado no arquivo
i.99
Versão não destruída:
fonte
JavaScript (ES6) 340
352Uma função com 2 parâmetros
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
fonte
q / k,
490469.
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)
f
lê o arquivo passado para o programa e remove caracteres desnecessáriosm
pega uma lista / vetor e multiplica os índices ímpares por -1b
é apenas uma função vazia, usada para as linhas sem operaçãop
é 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.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
f
lista de strings, comn
o iterador. A função goto será atualizadan
conforme necessário.fonte
Perl,
273 266 255 244238Quebras de linha adicionadas para maior clareza.
Nome do programa usado na linha de comando:
Cada linha do programa é convertida em código Perl, por exemplo:
Mais detalhes
fonte