Prindeal (pronunciado prin-dee-AL ) es una nueva esotérico lenguaje de programación que sólo tiene cuatro comandos: pr int , en Crement , de Crement y cols NIC . A pesar de su minimalismo, se pueden realizar operaciones matemáticas complejas en Prindeal combinando hábilmente los cuatro comandos.
Su tarea en este desafío de código de golf es escribir el programa más corto que pueda ejecutar el código de Prindeal.
La especificación es larga, pero he tratado de hacerlo lo más claro posible y creo que si te esfuerzas por aprender Prindeal, ¡te parecerá bastante elegante!
Intrepreting Prindeal
Preprocesamiento
Antes de poder interpretar un programa de Prindeal, es necesario eliminar estas cosas en este orden:
- Todo lo que está después de una
#
señal al final de la línea está encendido, más el#
propio. (Estos son comentarios) - Espacio en blanco al final de cualquier línea.
- Líneas completamente vacías.
Por ejemplo, el programa Prindeal
p cat #The next line has 7 trailing spaces.
p dog
#p mouse
sería preprocesado en
p cat
p dog
De aquí en adelante asumiremos que este paso de preprocesamiento se ha realizado.
Variables
Rápidamente necesitamos definir variables antes de mostrar cómo se usan.
Las variables (y las referencias a las variables) son lo que se pasa a los argumentos de los comandos de Prindeal. Las variables son siempre globales , por lo que las modificaciones a una variable, sin importar dónde ocurran, se reflejan en todas partes.
Cada variable contiene un entero de precisión arbitraria no negativo (0, 1, 2, 3, ...). Las variables no necesitan ser preinicializadas: siempre comienzan con el valor 0 la primera vez que se usan o se solicitan.
Un nombre de variable puede ser cualquier cadena no alfanumérica de caracteres alfanuméricos y subrayados que no comience con un dígito, [a-zA-Z_][0-9a-zA-Z_]*
en la expresión regular . Son sensibles a mayúsculas y minúsculas spiny_lumpsuck3r
y Spiny_lumpsuck3r
son variables diferentes.
Ejecución
Prindeal es un lenguaje de programación imperativo . Cuando se ejecuta un programa Prindeal, sus declaraciones se ejecutan de arriba a abajo en orden y luego el programa finaliza.
Cada línea no sangrada en un programa Prindeal es una declaración que involucra la ejecución de un solo comando que puede o no tomar argumentos.
Las líneas sangradas solo ocurren después de los comandos de alias . Específicamente, se producen exactamente tres líneas con sangría con espacios individuales después de cada comando de alias y se consideran parte de él. Entonces, las declaraciones de alias son realmente cuatro líneas de largo. (Podrían ser una línea, cuatro es simplemente más legible).
No alias Declaraciones
Con la excepción del alias , cada declaración en un programa Prindeal tiene la forma:
[command name] [argument 1] [argument 2] [argument 3] ...
Puede haber un número arbitrario de argumentos (incluido ninguno). Cada argumento es siempre una variable o (como veremos al discutir el alias ) una referencia a una variable .
Una vez finalizada la ejecución, cada instrucción se marca como una falla o éxito, dependiendo de si se encontraron errores o no. (Esto solo es realmente importante cuando usamos el alias ).
La impresión , el incremento y la disminución incorporados son declaraciones con el formulario anterior. Esto es lo que hacen:
print tiene nombre de comando
p
y toma un argumento. Imprime el nombre de la variable pasada y su valor (en decimal) separados por "=", luego una nueva línea. Siempre se marca como un éxito .Por ejemplo, el programa Prindeal
p _MyVariable_321 p screaming_hairy_armadillo
saldría
_MyVariable_321 = 0 screaming_hairy_armadillo = 0
porque todas las variables comienzan en 0. (Se requieren los espacios antes y después del signo igual).
incremento tiene nombre de comando
i
y toma un argumento. Incrementa el valor de la variable que se pasa en 1. Siempre se marca como un éxito .Por ejemplo, el programa
i alpaca p alpaca i alpaca p alpaca
saldría
alpaca = 1 alpaca = 2
Tenga en cuenta cómo
alpaca
se incrementó de 0 a 1 a pesar de que nunca antes se había accedido.decrement tiene nombre de comando
d
y toma un argumento. Si la variable que se pasa no es cero, su valor se reduce en 1 y la instrucción se marca como correcta . Si la variable que se pasa es 0, no se hace nada y la declaración se marca como un error .Por ejemplo, el programa
i malamute p malamute d malamute #success p malamute d malamute #failure p malamute d akita #failure p akita
saldría
malamute = 1 malamute = 0 malamute = 0 akita = 0
Tenga en cuenta que la disminución de una variable con valor 0 es la única forma de producir un error .
La declaración de alias y los comandos con alias
El comando alias tiene una sintaxis especial y es el más poderoso porque puede usarse para definir nuevos comandos. El nombre del comando alias es a
y una declaración de alias tiene la forma:
a [name of new command]
[statement A]
[statement B]
[statement C]
Donde cada uno [statement X]
representa cualquier declaración sin alias , es decir, algo con la forma [command name] [argument 1] [argument 2] [argument 3] ...
.
El nombre del comando con alias [name of new command]
puede ser cualquier cadena no alfanumérica de caracteres alfanuméricos y subrayados que no comience con un dígito, [a-zA-Z_][0-9a-zA-Z_]*
en expresiones regulares.
(Este es el mismo conjunto de nombres que las variables, pero los comandos con alias y las variables son cosas diferentes que se usan en diferentes lugares . Una variable podría denominarse igual que un comando sin consecuencias negativas).
Cuando se ejecuta una declaración de alias , se agrega un nuevo comando junto con los cuatro p
i
d
a
comandos originales . El nuevo comando se puede usar como [command name]
declaraciones in y se puede invocar con argumentos como cualquier otro comando sin alias .
Cuando se ejecuta una declaración con un nombre de comando con alias, se ejecutan exactamente dos declaraciones más de su declaración de alias original :
[statement A]
siempre se ejecuta[statement B]
se ejecuta si[statement A]
fue un éxito[statement C]
se ejecuta si[statement A]
fue un error
Las declaraciones A, B y C siempre se ejecutan perezosamente , es decir, se evalúan sobre la marcha en el momento en que se ejecutan.
Cuando finaliza la ejecución, el comando con alias se marca con el mismo indicador de éxito o error que la instrucción B o C, cualquiera que se haya ejecutado . (las declaraciones de alias no necesitan ser marcadas ya que no pueden ocurrir dentro de ellas mismas).
Alias Ejemplo 1
Digamos que queremos un nuevo comando que incremente la variable
frog
dos veces. Esta declaración de alias lo logra:a increment_frog_twice i frog i frog d frog
La instrucción A (
i frog
) siempre se ejecuta y siempre se marca como un éxito, por lo que la instrucción B (i frog
) también se ejecuta siempre y, por lo tanto, la variablefrog
se incrementa en 2. Elincrement_frog_twice
comando siempre se marca como un éxito porque la instrucción B siempre se ejecuta y B siempre es un el éxito . La instrucción C (d frog
) nunca se ejecuta.Entonces la salida a
a increment_frog_twice i frog i frog d frog p frog increment_frog_twice p frog
sería
frog = 0 frog = 2
Podemos generalizar este ejemplo para que cualquier variable pueda incrementarse dos veces dando un argumento al comando con alias.
Dentro de una declaración de alias , los enteros positivos 1, 2, 3, etc. representan los argumentos primero, segundo, tercero, etc. pasados al comando con alias. (Estos argumentos pueden ser variables simples o referencias a variables en sí mismas). Estos números solo pueden aparecer dentro de los argumentos de la declaración A, B y C en una declaración de alias . No tiene sentido que aparezcan en otro lugar.
Alias Ejemplo 2
Esto generaliza el último ejemplo: cualquier variable pasada
increment_twice
se incrementará en 2 porque1
es una referencia al primer argumento pasado:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toad
El resultado de este programa sería
toad = 0 toad = 2
Podríamos alias otro comando que tome dos argumentos y los invoque
increment_twice
a ambos: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
La salida aquí sería
platypus = 2 duck = 2
Es importante darse cuenta de que los comandos con alias pueden ser recursivos, ya que aquí es donde radica su verdadero poder. Por ejemplo, podemos hacer un comando que establezca cualquier variable pasada a 0:
Alias Ejemplo 3
El
set_to_zero
comando toma un argumento y establece su variable en 0 y se marca como un éxito cuando se hace: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
El resultado de este programa sería
oryx = 3 oryx = 0
Lo que sucede es que cuando
set_to_zero oryx
se ejecuta,d 1
disminuye con éxitooryx
de 3 a 2, luegoset_to_zero 1
se llama, que es lo mismo que llamar deset_to_zero oryx
nuevo. Por lo tanto, el proceso se repite hasta que sed 1
produce un error , deteniendo la recursividad e incrementando la_dummy_
variable para que se produzca un éxito .
Reto
Escriba un programa que pueda ejecutar el código de Prindeal exactamente como se describe anteriormente. Tome el código de Prindeal mediante stdin, la línea de comando o como un archivo de texto. Imprima la salida del programa Prindeal en stdout o la alternativa más cercana a su idioma.
Alternativamente, puede escribir una función que tome el código como una cadena e imprima o devuelva la cadena de salida.
Además, puede suponer que:
- El código de entrada de Prindeal solo contendrá nuevas líneas y ASCII imprimible y (opcionalmente) que termina con una línea vacía.
- El código de entrada será válido Prindeal, bien formado y sintácticamente correcto.
- Ejecutar el código no producirá ningún bucle infinito ni referencias inválidas a comandos que no han sido definidos o argumentos que no han sido dados.
- Los nombres de los comandos
p
,i
,d
, ya
nunca serán más de alias. (Es posible que no se asuma que las variables no tendrán estos nombres.)
Además, no importa si los valores de sus variables no son enteros de precisión arbitraria, ya que solo se probarán números menores de 1000. También está bien si su idioma tiene límites de recursión (como Python ) que los programas Prindeal más complejos pueden encontrar mientras el programa de prueba a continuación funcione.
Programa de prueba
Aquí hay un gran programa Prindeal que desarrolla las operaciones de suma, multiplicación y exponenciación mediante el uso de variables ficticias (comenzando _
por convención) y muchos alias 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
(Si está jugando con este código, tenga en cuenta que muchos de los comandos fallarán si la misma variable se da varias veces como argumento. Esto se puede solucionar fácilmente pero el código resultante es más largo).
Su intérprete Prindeal debería poder producir la salida exacta:
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
Tanteo
El código más corto en bytes gana. Tiebreaker va a la presentación anterior.
Brownie Bonus: escribe un programa genial en Prindeal. Implementé la suma y la multiplicación, ¿puedes hacer resta o división?
p
, y luegop p
, ¿cuál imprimiría 1, verdad?