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_lumpsuck3ry Spiny_lumpsuck3rson 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
py 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_armadillosaldría
_MyVariable_321 = 0 screaming_hairy_armadillo = 0porque todas las variables comienzan en 0. (Se requieren los espacios antes y después del signo igual).
incremento tiene nombre de comando
iy 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 alpacasaldría
alpaca = 1 alpaca = 2Tenga en cuenta cómo
alpacase incrementó de 0 a 1 a pesar de que nunca antes se había accedido.decrement tiene nombre de comando
dy 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 akitasaldría
malamute = 1 malamute = 0 malamute = 0 akita = 0Tenga 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 ay 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 acomandos 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
frogdos veces. Esta declaración de alias lo logra:a increment_frog_twice i frog i frog d frogLa 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 variablefrogse incrementa en 2. Elincrement_frog_twicecomando 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 frogserí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_twicese incrementará en 2 porque1es una referencia al primer argumento pasado:a increment_twice i 1 i 1 d 1 #never reached p toad increment_twice toad p toadEl resultado de este programa sería
toad = 0 toad = 2Podríamos alias otro comando que tome dos argumentos y los invoque
increment_twicea 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 duckLa 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_zerocomando 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 oryxEl resultado de este programa sería
oryx = 3 oryx = 0Lo que sucede es que cuando
set_to_zero oryxse ejecuta,d 1disminuye con éxitooryxde 3 a 2, luegoset_to_zero 1se llama, que es lo mismo que llamar deset_to_zero oryxnuevo. Por lo tanto, el proceso se repite hasta que sed 1produce 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, yanunca 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?
fuente

p, y luegop p, ¿cuál imprimiría 1, verdad?Respuestas:
Pyth,
162136 bytesDemostración.
Golfed fuera 26 caracteres por inlining variables, y se cambia de
IyEflujo de control basado a?y.xflujo de control basado.Por primera vez, me quedé sin variables en Pyth. Todas las variables en Pyth (
bdkGHNTYyJK) estaban en uso, y quería usarlasbcomo una nueva línea. Afortunadamente, pude usarNpara significar dos cosas completamente diferentes en diferentes partes del programa, y aún así funciona.Sin golf (ejecutar con -m):
fuente
Python 2,
600584397373 bytesEsta es mi propia solución de referencia de golf. Cualquiera puede mejorarlo o seguir su lógica en su propia respuesta, siempre y cuando se otorgue la atribución.
Lo bueno de esto es que no se hace recursión, por lo que nunca tendrá problemas con el límite de recursión de Python. Por ejemplo, el programa Spup Countup Prindeal puede ejecutarse indefinidamente.
Es un programa que toma la cadena de programa citada con líneas nuevas escapadas, por ejemplo
'p _MyVariable_321\np screaming_hairy_armadillo'.Tomé varias pistas de golf de las respuestas de Sp y Pietu . Gracias chicos :)
fuente
Python 3,
345336335328 bytes(-6 bytes gracias a @orlp)
Todavía jugando al golf. Asume que el programa está almacenado en un archivo llamado
P.Poner las llamadas
fdentro del lambdadahorraría unos pocos bytes, pero haría que el último caso de prueba alcanzara la profundidad máxima de recursión.Algunos programas de Prindeal
El programa de sustracción inútil
Aquí hay un programa de resta inútil . Es inútil porque, aunque resta correctamente, no devuelve el éxito / fracaso en consecuencia.
La salida debe ser:
Contar hasta
Cuenta hacia arriba e imprime
npara siempre. Posiblemente podría funcionar como una prueba para la velocidad del intérprete (tenga cuidado con las trazas largas en la interrupción del teclado)fuente
l[:(l+"#").find("#")]y todas sus variaciones pueden ser reemplazadas por una simplel.split('#')[0].findque olvidé que podríassplitincluso si#no estuvieras allí. Gracias :)JavaScript (ES6), 273
258Editó errores solucionados y agregó un conjunto de pruebas real.
Sin contar los espacios iniciales y las nuevas líneas.
Seguramente se puede jugar un poco más al golf.
Demasiado cansado para escribir una explicación ahora, creo que es un buen ejemplo del uso de cierres para mantener vivos los valores temporales (parámetros).
Pruebe a ejecutar el fragmento en cualquier navegador compatible con EcmaScript 6 (en particular, no Chrome ni MSIE. Probé en Firefox, Safari 9 podría funcionar)
fuente
C # 6, 653 bytes
Aquí está mi entrada, en medio de un mar de Python ...
Ampliado y comentado:
Para usarlo, simplemente crea una instancia de la clase y llama al
R()método, por ejemplo:fuente
Lisp común,
758646619Pon esto
file.lispy llama por ejemplosbcl --script file.lisp; la entrada se lee de la secuencia de entrada estándar.Esta versión analiza un superconjunto de Prindeal: sin muchas dificultades, puede acceder a Common Lisp desde una fuente de Prindeal. Considero que esto es una característica del intérprete.
Versión comentada
Ejemplo
Si reemplazamos
evalporprinten el ciclo de lectura / evaluación, entonces podemos ver lo que se está evaluando:Macroexpansion
Si elegimos la siguiente definición de alias:
... podemos ver referencias a una variable llamada
gque no se encuentra en ninguna parte del ámbito léxico. Pero después de la macroexpansión, aquí está el código real que se está evaluando:Ahora, se
grefiere a la lista de argumentos de la función que se está definiendo.fuente
Python 2, 486 bytes
Esta es la solución de referencia que jugué más (actualmente -98 bytes).
Cambios (que recuerdo):
[l,l[:l.find('#')]]['#'in l]).V[k]=-~V[k]if k in V else 1)k=s[1])printAgregar espacios automáticamente (print k,'=',V.get(k,0))'0'<t[0]<':')ralrededor para guardarreturnsmap(str.split,c[:3])))fuente
Python 3, 1322 bytes
Golfizado:
Sin golf:
Uso:
¿Dónde
cestá el contenido del texto?Ejemplos:
Se aceptan cadenas de una línea:
P("p cat")P("p dog\ni dog\np dog")También se aceptan cuerdas con líneas múltiples:
O:
Etc.
Notas:
Esto funciona correctamente para todos los casos de prueba, pero alcanza el límite de recurrencia en:
De ahí el
sys.setrecursionlimit(2000).fuente
Python -
695688 bytes<TAB>es un carácter de tabulación literal.fuente
C ++, 1111 bytes
Este es C ++, tan idiomático como podría hacerlo.
Eso significa hacerlo más C ++ - ish y menos C-ish.
Eso también significa que es más grande que el programa C equivalente.
Creo que C ++ rivaliza con Java para la biblioteca estándar detallada.
Se compila con VS2013 y g ++ 4.9.2 (con -std = c ++ 11)
Debajo está el original. Si alguien puede pensar en una forma de hacerlo más idiomático y más corto al mismo tiempo, hágamelo saber.
fuente
Haskell, 1009
Hice mi mejor esfuerzo para jugar golf; mi código sin golf consistía en más de 3,000 caracteres. En este punto no puedo recordar qué hacen todas las funciones, por lo que jugar al golf significa adivinar qué lo romperá y qué no.
fuente