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?
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
I
yE
flujo de control basado a?
y.x
flujo de control basado.Por primera vez, me quedé sin variables en Pyth. Todas las variables en Pyth (
bdkGHNTY
yJK
) estaban en uso, y quería usarlasb
como una nueva línea. Afortunadamente, pude usarN
para 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
f
dentro del lambdad
ahorrarí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
n
para 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]
.find
que olvidé que podríassplit
incluso 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.lisp
y 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
eval
porprint
en 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
g
que 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
g
refiere 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]
)print
Agregar espacios automáticamente (print k,'=',V.get(k,0)
)'0'<t[0]<':'
)r
alrededor para guardarreturn
smap(str.split,c[:3]))
)fuente
Python 3, 1322 bytes
Golfizado:
Sin golf:
Uso:
¿Dónde
c
está 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