Prindeal (pronunciado prin-dee-al ) é uma nova esotérica linguagem de programação que só tem quatro comandos: pr int , em Crement , de Crement , e ao IAS . Apesar de seu minimalismo, operações matemáticas complexas podem ser feitas no Prindeal combinando inteligentemente os quatro comandos.
Sua tarefa neste desafio de código de golfe é escrever o programa mais curto que pode executar o código do Prindeal.
A especificação é longa, mas tentei deixar o mais claro possível e acredito que se você se esforçar para aprender o Prindeal, verá que é bastante elegante!
Intrepreting Prindeal
Pré-processando
Antes que um programa Prindeal possa ser interpretado, é necessário removê-lo da seguinte maneira:
- Qualquer coisa após um
#
sinal até o final da linha está ligado, mais o#
próprio. (Estes são comentários.) - Espaço em branco à direita em qualquer linha.
- Linhas completamente vazias.
Por exemplo, o programa Prindeal
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
seria pré-processado em
p cat
p dog
A partir daqui, assumiremos que esta etapa de pré-processamento foi concluída.
Variáveis
Rapidamente precisamos definir variáveis antes de mostrar como elas são usadas.
Variáveis (e referências a variáveis) são o que são passados para os argumentos dos comandos do Prindeal. As variáveis são sempre globais ; portanto, as modificações em uma variável, não importa onde elas ocorram, são refletidas em todos os lugares.
Cada variável possui um número inteiro de precisão arbitrária não negativo (0, 1, 2, 3, ...). As variáveis não precisam ser pré-inicializadas - elas sempre começam com o valor 0 na primeira vez em que são usadas ou solicitadas.
Um nome de variável pode ser qualquer sequência não alfanumérica e sublinhada que não comece com um dígito - [a-zA-Z_][0-9a-zA-Z_]*
em regex . Eles diferenciam maiúsculas de minúsculas spiny_lumpsuck3r
e Spiny_lumpsuck3r
são variáveis diferentes.
Execução
Prindeal é uma linguagem de programação imperativa . Quando um programa Prindeal é executado, suas instruções são executadas de cima para baixo em ordem e, em seguida, o programa termina.
Toda linha não recuada em um programa Prindeal é uma declaração que envolve a execução de um único comando que pode ou não receber argumentos.
Linhas recuadas ocorrem somente após comandos de alias . Especificamente, exatamente três linhas recuadas com espaços únicos ocorrem após cada comando de alias e são consideradas parte dele. Portanto, as declarações de alias têm realmente quatro linhas. (Eles podem ser uma linha, quatro é apenas mais legível.)
Não de alias Demonstrações
Com exceção do alias , todas as instruções em um programa Prindeal têm a forma:
[command name] [argument 1] [argument 2] [argument 3] ...
Pode haver um número arbitrário de argumentos (incluindo nenhum). Cada argumento é sempre uma variável ou (como veremos ao discutir o alias ) uma referência a uma variável .
Uma vez concluída a execução, cada instrução é sinalizada como uma falha ou êxito, dependendo se erros foram encontrados ou não. (Isso realmente importa apenas quando usamos o alias .)
A impressão interna , incremento e decremento são instruções com o formulário acima. Aqui está o que eles fazem:
print tem nome de comando
p
e aceita um argumento. Ele imprime o nome da variável transmitida e seu valor (em decimal) separado por "=" e, em seguida, uma nova linha. É sempre sinalizado como sucesso .Por exemplo, o programa Prindeal
p _MyVariable_321 p screaming_hairy_armadillo
produziria
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
porque todas as variáveis começam em 0. (Os espaços antes e depois do sinal de igual são necessários.)
incremento tem o nome do comando
i
e leva um argumento. Ele incrementa o valor da variável passada por 1. Ele é sempre sinalizado como um sucesso .Por exemplo, o programa
i alpaca p alpaca i alpaca p alpaca
produziria
alpaca = 1 alpaca = 2
Observe como
alpaca
foi incrementado de 0 para 1, mesmo que nunca tivesse sido acessado antes.decrement tem nome de comando
d
e usa um argumento. Se a variável transmitida for diferente de zero, seu valor será decrementado por 1 e a instrução será sinalizada como um sucesso . Se a variável passada for 0, nada será feito e a instrução será sinalizada como uma falha .Por exemplo, o programa
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
produziria
malamute = 1 malamute = 0 malamute = 0 akita = 0
Observe que decrementar uma variável com o valor 0 é a única maneira de produzir uma falha .
O alias instrução e comandos alias
O comando alias possui uma sintaxe especial e é o mais poderoso, pois pode ser usado para definir novos comandos. O nome do comando alias é a
e uma instrução alias tem o formato:
a [name of new command]
[statement A]
[statement B]
[statement C]
Onde cada um [statement X]
representa qualquer declaração de não- alias , ou seja, algo com o formulário[command name] [argument 1] [argument 2] [argument 3] ...
.
O nome do comando com alias [name of new command]
pode ser qualquer sequência de caracteres alfanuméricos e sublinhados não vazia que não comece com um dígito -[a-zA-Z_][0-9a-zA-Z_]*
na regex.
(Esse é o mesmo conjunto de nomes das variáveis, mas comandos e variáveis com alias são coisas diferentes usadas em lugares diferentes . Uma variável pode ter o mesmo nome de um comando, sem consequências ruins.)
Quando uma instrução de alias é executada, um novo comando é adicionado ao lado dos quatro p
i
d
a
comandos originais . O novo comando pode ser usado como as [command name]
instruções in e chamado com argumentos como qualquer outro não- alias comando .
Quando uma instrução com um nome de comando com alias é executada, são executadas exatamente mais duas instruções de sua instrução de alias original :
[statement A]
é sempre executado[statement B]
é executado se[statement A]
foi um sucesso[statement C]
é executado se[statement A]
houve uma falha
As instruções A, B e C são sempre executadas preguiçosamente , ou seja, são avaliadas em tempo real no momento em que são executadas.
Quando a execução é concluída, o comando alternativo é sinalizado com o mesmo sinalizador de êxito ou falha da instrução B ou C, o que tiver sido executado . (As declarações de alias em si não precisam ser sinalizadas, pois não podem ocorrer dentro de si.)
Exemplo alternativo 1
Digamos que queremos um novo comando que aumente a variável
frog
duas vezes. Esta declaração de alias alcança:a increment_frog_twice i frog i frog d frog
A instrução A (
i frog
) é sempre executada e sempre sinalizada como um sucesso, portanto, a instrução B (i frog
) também é sempre executada e a variávelfrog
é incrementada em 2. Oincrement_frog_twice
comando é sempre sinalizado como um sucesso porque a instrução B é sempre executada e B é sempre uma variável. sucesso . A instrução C (d frog
) nunca é executada.Então a saída para
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
seria
frog = 0 frog = 2
Podemos generalizar este exemplo para que qualquer variável possa ser incrementada duas vezes, dando um argumento ao comando aliasado.
Dentro de uma declaração de alias , os números inteiros positivos 1, 2, 3, etc. representam os argumentos do 1º, 2º, 3º etc. passados para o comando do alias. (Esses argumentos podem ser variáveis simples ou referências às próprias variáveis.) Esses números podem aparecer apenas dentro dos argumentos da instrução A, B e C em uma instrução de alias . Não faz sentido que eles apareçam em outro lugar.
Exemplo alternativo 2
Isso generaliza o último exemplo - qualquer variável passada
increment_twice
será incrementada por 2 porque1
é uma referência ao primeiro argumento passado em:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
A saída deste programa seria
toad = 0 toad = 2
Poderíamos, então, alias outro comando que recebe dois argumentos e chama os
increment_twice
dois:a increment_twice i 1 i 1 d 1 #never reached a increment_both_twice increment_twice 1 increment_twice 2 d 1 #never reached increment_both_twice platypus duck p platypus p duck
A saída aqui seria
platypus = 2 duck = 2
É importante perceber que os comandos com alias podem ser recursivos, pois é aí que reside o verdadeiro poder deles. Por exemplo, podemos criar um comando que define qualquer variável passada para 0:
Exemplo alternativo 3
O
set_to_zero
comando pega um argumento e define sua variável como 0 e é sinalizado como um sucesso quando concluído:a set_to_zero d 1 set_to_zero 1 i _dummy_ i oryx i oryx i oryx p oryx set_to_zero oryx p oryx
A saída deste programa seria
oryx = 3 oryx = 0
O que está acontecendo é que, quando
set_to_zero oryx
executado,d 1
diminui com êxitooryx
de 3 para 2 eset_to_zero 1
é chamado, o mesmo que chamarset_to_zero oryx
novamente. Portanto, o processo se repete até que hajad 1
uma falha , interrompendo a recursão e incrementando a_dummy_
variável para que um sucesso seja produzido.
Desafio
Escreva um programa que possa executar o código do Prindeal exatamente como descrito acima. Pegue o código do Prindeal via stdin, na linha de comando ou como um arquivo de texto. Imprima a saída do programa Prindeal para stdout ou a alternativa mais próxima do seu idioma.
Como alternativa, você pode escrever uma função que recebe o código como uma sequência e imprime ou retorna a sequência de saída.
Além disso, você pode assumir que:
- O código Prindeal de entrada conterá apenas novas linhas e ASCII imprimíveis e (opcionalmente) que termina com uma linha vazia.
- O código de entrada será válido no Prindeal - bem formado e sintaticamente correto.
- A execução do código não produzirá loops infinitos nem referências inválidas a comandos que não foram definidos ou argumentos que não foram fornecidos.
- Os nomes de comando
p
,i
,d
ea
nunca será alias mais. (Você não pode assumir que variáveis não terão esses nomes.)
Além disso, não importa se os valores das variáveis não são números inteiros com precisão arbitrária, pois apenas números menores que 1000 serão testados. Também é bom se a linguagem tiver limites de recursão (como Python ) que os programas mais complexos do Prindeal podem encontrar, desde que o programa de teste abaixo funcione.
Programa de teste
Aqui está um grande programa do Prindeal que constrói as operações de adição, multiplicação e exponenciação através do uso de variáveis fictícias (começando _
por convenção) e muitos aliases auxiliares:
#Command Definitions:
a s #flag as a success
i _
d _
d _
a f #flag as a failure
d _
d _
d _
a z #1 = zero
d 1
z 1
s
a n #1 = one
z 1
i 1
s
a move #2 += 1, 1 = zero
moveH 1 2
move 1 2
s
a moveH #move helper
d 1
i 2
f
a dupe #2 += 1, 3 += 1, 1 = zero
dupeH1 1 2 3
dupe 1 2 3
s
a dupeH1 #dupe helper
d 1
dupeH2 2 3
f
a dupeH2 #dupe helper
i 1
i 2
s
a copy #2 = 1
z 2
copyH 1 2
s
a copyH #copy helper
dupe 1 2 _copy
move _copy 1
s
a addTo #1 += 2
copy 2 _add
#testing comments #
move _add 1#in weird places # just because #
s
#it's a g##d idea
###
a add #1 = 2 + 3
#its a good idea
z 1
addH 1 2 3
s
##
#
a addH #add helper
#this is a comment
addTo 1 2 #as is this
addTo 1 3
s
a mul #1 = 2 * 3
mulH1 1 2
mulH2 1 3
s
a mulH1 #mul helper
z 1
copy 2 _mul
s
a mulH2 #mul helper
mulH3 1 2
mulH2 1 2
s
a mulH3 #mul helper
d _mul
addTo 1 2
f
a mulBy #1 *= 2
mul _mulBy 1 2
copy _mulBy 1
s
a pow #1 = 2^3
powH1 1 3
powH2 1 2
s
a powH1 #pow helper
n 1
copy 2 _pow
s
a powH2 #pow helper
powH3 1 2
powH2 1 2
s
a powH3 #pow helper
d _pow
mulBy 1 2
f
#Running Tests:
p A
p B
p C
n A #A = 1
n B #B = 1
add C A B #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B #d = d * B = 6 * 3 = 18
p ____
p d
d A #A = A - 1 = 1 - 1 = 0
mulBy d A #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C
(Se você estiver brincando com esse código, saiba que muitos dos comandos falharão se a mesma variável for dada várias vezes como argumento. Isso pode ser facilmente corrigido, mas o código resultante é mais longo.)
Seu intérprete Prindeal deve ser capaz de produzir a saída exata:
A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729
Pontuação
O código mais curto em bytes vence. O desempatador vai para a submissão anterior.
Brownie Bonus: Escreva um programa interessante no Prindeal. Eu implementei adição e multiplicação, você pode fazer subtração ou divisão?
fonte
p
, e entãop p
, o que imprimiria 1, certo?Respostas:
Pitão,
162136 bytesDemonstração.
Golfed para 26 caracteres por inlining variáveis e mudando a partir de
I
eE
com base fluxo de controlo para?
e.x
fluxo de controle baseado.Pela primeira vez, fiquei sem variáveis em Pyth. Todas as variáveis em Pyth (
bdkGHNTY
eJK
) estavam em uso, e eu queria usarb
como uma nova linha. Felizmente, eu era capaz de usarN
significar duas coisas completamente diferentes em partes diferentes do programa, e assim ainda funciona.Ungolfed (execute com -m):
fonte
Python 2,
600584397373 bytesEsta é a minha própria solução de referência para golfe. Qualquer pessoa é bem-vinda para melhorá-lo ou seguir sua lógica em sua própria resposta, desde que seja atribuída.
A parte interessante é que nenhuma recursão é feita, portanto nunca haverá problemas com o limite de recursão do Python. Por exemplo, o programa Countup Prindeal da Sp pode ser executado indefinidamente.
É um programa que recebe a sequência de programa citada com novas linhas escapadas, por exemplo
'p _MyVariable_321\np screaming_hairy_armadillo'
.Peguei várias pistas de golfe das respostas de Sp e Pietu . Obrigado rapazes :)
fonte
Python 3,
345336335328 bytes(-6 bytes graças a @orlp)
Ainda jogando golfe. Assume que o programa está armazenado em um arquivo chamado
P
.Colocar as chamadas
f
dentro do lambdad
economizaria alguns bytes, mas faria com que o último caso de teste atingisse a profundidade máxima de recursão.Alguns programas Prindeal
O programa de subtração inútil
Aqui está um programa de subtração inútil . É inútil porque, mesmo que subtraia corretamente, não retorna sucesso / falha de acordo.
A saída deve ser:
Countup
Conta para cima e imprime
n
para sempre. Poderia funcionar como um teste de velocidade do intérprete (cuidado com os longos rastros na interrupção do teclado).fonte
l[:(l+"#").find("#")]
e todas as suas variações podem ser substituídas por uma simplesl.split('#')[0]
.find
que eu esqueci que você poderia,split
mesmo que#
não estivesse lá. Obrigado :)JavaScript (ES6), 273
258Editar bugs corrigidos e adicionou um conjunto de testes real.
Sem contar espaços iniciais e novas linhas.
Certamente pode ser jogado um pouco mais.
Cansado demais para escrever uma explicação agora, acho que é um bom exemplo de uso de fechamentos para manter vivos valores temporários (parâmetros).
Teste a execução do snippet em qualquer navegador compatível com EcmaScript 6 (principalmente o Chrome, não o MSIE. Eu testei no Firefox, o Safari 9 poderia ir)
fonte
C # 6, 653 bytes
Aqui está minha entrada, em meio a um mar de Python ...
Expandido e comentado:
Para usá-lo, basta instanciar a classe e chamar o
R()
método, por exemplo:fonte
Lisp comum,
758646619Coloque isso
file.lisp
e chame por exemplosbcl --script file.lisp
; entrada é lida a partir do fluxo de entrada padrão.Esta versão analisa um superconjunto do Prindeal: sem muitas dificuldades, você pode acessar todo o Common Lisp a partir de uma fonte do Prindeal. Considero isso uma característica do intérprete.
Versão comentada
Exemplo
Se substituirmos
eval
porprint
no loop de leitura / avaliação, podemos ver o que está sendo avaliado:Expansão de macro
Se escolhermos a seguinte definição de alias:
... podemos ver referências a uma variável nomeada
g
que não pode ser encontrada em nenhum lugar no escopo lexical. Mas após a macroexpansão, eis o código real que está sendo avaliado:Agora,
g
refere-se à lista de argumentos da função que está sendo definida.fonte
Python 2, 486 bytes
Esta é a solução de referência que eu joguei mais (atualmente -98 bytes).
Mudanças (que eu me lembro):
[l,l[:l.find('#')]]['#'in l]
).V[k]=-~V[k]if k in V else 1
)k=s[1]
)print
adicionando espaços automaticamente (print k,'=',V.get(k,0)
)'0'<t[0]<':'
)r
volta para salvarreturn
smap(str.split,c[:3]))
)fonte
Python 3, 1322 bytes
Golfe:
Ungolfed:
Uso:
Onde
c
está o conteúdo do texto.Exemplos:
Cadeias de caracteres de linha única são aceitas:
P("p cat")
P("p dog\ni dog\np dog")
Também são aceitas sequências de linhas múltiplas:
Ou:
Etc.
Notas:
Isso funciona corretamente para todos os casos de teste, mas atinge o limite de recursão em:
Daí o
sys.setrecursionlimit(2000)
.fonte
Python -
695688 bytes<TAB>
é um caractere de tabulação literal.fonte
C ++, 1111 bytes
Este é o C ++ - o mais idiomático possível.
Isso significa torná-lo mais C ++ - ish e menos C-ish.
Isso também significa que é maior que o programa C equivalente.
Eu acho que C ++ rivaliza com Java para biblioteca padrão detalhada.
Ele é compilado com o VS2013 e g ++ 4.9.2 (com -std = c ++ 11)
Abaixo está o original. Se alguém puder pensar em uma maneira de torná-lo mais idiomático e mais curto ao mesmo tempo, informe-me.
fonte
Haskell, 1009
Eu fiz o meu melhor para jogar golfe; meu código não destruído consistia em mais de 3.000 caracteres. Neste ponto, não me lembro do que todas as funções estão fazendo, portanto, jogar golfe significa adivinhar o que irá quebrá-lo e o que não.
fonte