Usa la tercera pila
Si ha leído el título, puede estar un poco confundido. ¿Seguramente solo hay dos pilas en Brain-Flak? Sin embargo, le aseguro que existe y que es una de las herramientas más poderosas, si no la más poderosa, para escribir y jugar Brain-Flak.
¿Qué es la "tercera pila"?
Cada programa Brain-Flak usa la tercera pila de una forma u otra, pero la mayor parte del uso se lleva a cabo detrás de escena y a menudo es útil simplemente ignorar el hecho de que existe. Cada paréntesis en el programa agrega o elimina un elemento de la pila. Tres de las llaves abiertas ([<
agregan un elemento a la pila, mientras que sus tres conjugados )]>
eliminan un elemento de la pila. El valor del elemento en la pila es el valor del alcance actual del programa y el uso de nilads modificará este valor de ciertas maneras. El paréntesis cerrado )
tiene la función única de mover un elemento de la tercera pila a la pila actual; un empujón.
Esperemos que esto te resulte claro. La tercera pila es una especie de pila que recuerda los valores de retorno del código que ya se ha ejecutado. Veamos un ejemplo de un programa simple que realiza un seguimiento de las dos pilas normales y la tercera pila.
Ejemplo
Recorreremos el siguiente programa. Este programa empuja -3, 1, -2
a la pila.
(([()()()])(()))
Comenzamos con tres llaves abiertas, que empujan un cero a la tercera pila.
Nuestras pilas ahora se ven así, la tercera pila es la de la derecha y la pila activa tiene ^
debajo:
0
0
0 0 0
^
(([()()()])(()))
^
Ahora tenemos tres ()
nilas. Estos no hacen nada a las dos pilas normales, sin embargo, cada una agrega una a la parte superior de la tercera pila haciendo que nuestras pilas se vean así:
3
0
0 0 0
^
(([()()()])(()))
^
Ahora encontramos que, ]
como se indicó antes, las llaves cerradas eliminan un elemento de la tercera pila, pero ]
tiene la función de restar el elemento que elimina de la parte superior de la pila. Así nuestras nuevas pilas se verán así:
-3
0 0 0
^
(([()()()])(()))
^
Esto tiene sentido; [...]
hace negación por lo que ]
debería restar hacia abajo.
Ahora debemos ejecutar a )
. Como probablemente recuerde, )
es el lugar en el programa donde las cosas se envían a la pila, por lo que moveremos la parte superior de la tercera pila a la pila actual, además agregaremos el -3
elemento siguiente en la tercera pila.
-3 0 -3
^
(([()()()])(()))
^
Una vez más nos encontramos con una de nuestras tres llaves abiertas, por lo que agregaremos otro elemento a nuestra tercera pila.
0
-3 0 -3
^
(([()()()])(()))
^
Como dijimos anteriormente, ()
aumentará la parte superior de nuestra tercera pila en uno.
1
-3 0 -3
^
(([()()()])(()))
^
Y )
moverá la parte superior de la tercera pila a la pila activa y agregará hacia abajo
1
-3 0 -2
^
(([()()()])(()))
^
El último )
mueve la tercera pila a la pila activa y, dado que no quedan elementos en la tercera pila para agregar, no hace nada más.
-2
1
-3 0
^
(([()()()])(()))
^
El programa ha terminado, así que terminamos y damos salida.
Este ejemplo tiene la intención de darle una idea de lo que es y hace el Third Stack. No incluye todas las operaciones, pero es de esperar que pueda descubrir qué hace cada una por sí solo. Si todavía tiene dificultades, he incluido una "hoja de trucos" en la parte inferior de esta respuesta para ayudarlo.
¿OK y eso qué?
Ok, ahora entiendes la tercera pila, pero "¿Y qué?" Ya lo estaba utilizando, incluso si no lo llamaba "Third Stack", ¿cómo le ayuda el golf en términos de Third Stack?
Veamos un problema. Desea tomar el triángulo de un número . Esta es la suma de todos los números menores que n.
Un enfoque podría ser crear un acumulador en la pila y agregarlo a medida que realiza la cuenta regresiva. Esto crea un código que se ve así:
(<>)<>{(({}[()])()<>{})<>}{}<>({}<>)
Pruébalo en línea!
Este código es bastante compacto y uno podría pensar que no puede ser mucho más pequeño. Sin embargo, si lo abordamos desde un punto de vista de la tercera pila, queda claro que esto es extremadamente ineficiente. En lugar de poner nuestro acumulador en la pila, podemos colocarlo en la tercera pila con un (
y recuperarlo al final que usamos )
. Una vez más, recorreremos todos los números, pero esta vez no tenemos que hacer nada para incrementar nuestra tercera pila, el programa lo hace por nosotros. Esto se ve así:
({()({}[()])}{})
Pruébalo en línea
Este código es menos de la mitad del tamaño de la versión bastante bien desarrollada que hicimos antes. De hecho, una búsqueda por computadora ha demostrado que este programa es el programa más corto posible que puede realizar esta tarea. Este programa se puede explicar utilizando el enfoque de "suma de todas las ejecuciones", pero creo que es mucho más intuitivo y claro cuando se explica utilizando un enfoque de Third Stack.
¿Cuándo uso la tercera pila?
Idealmente, cada vez que comience a trabajar en un nuevo problema en Brain-Flak, debe pensar cómo haría esto con la Tercera Pila en mente. Sin embargo, como regla general, siempre que tenga que realizar un seguimiento de algún tipo de acumulador o tenga un total acumulado, es una buena idea intentar colocarlo en su tercera pila en lugar de las dos pilas reales.
Otra vez que podría ser una buena idea considerar usar su tercera pila es cuando no tiene espacio para almacenar algún valor en las otras dos pilas. Esto puede ser particularmente útil cuando realiza manipulaciones en dos pilas existentes y desea guardar un valor para su uso posterior sin tener que realizar un seguimiento de dónde está.
Limitaciones de la tercera pila
El Third Stack es muy poderoso en muchos sentidos, pero viene con sus propias limitaciones e inconvenientes.
En primer lugar, la altura máxima de la pila para la tercera pila en cualquier punto determinado se determina en el momento de la compilación. Esto significa que si desea utilizar una cantidad de espacio en la pila, debe asignar ese espacio cuando está escribiendo el programa.
En segundo lugar, la tercera pila no es el acceso aleatorio. Esto significa que no puede realizar ninguna operación en ningún valor que no sea el valor más alto. Además, no puede mover valores en la pila (por ejemplo, intercambie los dos primeros elementos).
Conclusión
The Third Stack es una herramienta poderosa y lo consideraría esencial para todos los usuarios de Brain-Flak. Toma un tiempo acostumbrarse y requiere un cambio en la forma en que piensas sobre la programación en Brain-Flak, pero cuando se usa correctamente, hace toda la diferencia entre un juego decente y uno increíble cuando se trata de jugar al golf.
Hoja de trucos
Aquí hay una lista de operaciones y cómo afectan la tercera pila
Operation | Action
====================================================
(,[,< | Put a zero on top of the Third Stack
----------------------------------------------------
) | Add the top of the Third Stack to the
| second element and move it to the
| active stack
----------------------------------------------------
] | Subtract the top of the Third Stack
| from the second element and pop it
----------------------------------------------------
> | Pop the top of the Third Stack
----------------------------------------------------
() | Add one to the top of the Third Stack
----------------------------------------------------
{} | Pop the top of the active stack and
| add it to the top of the Third Stack
----------------------------------------------------
[] | Add the stack height to the Third
| Stack
----------------------------------------------------
<>,{,} | Nothing
{...}
devuelve la suma de todas las iteraciones.<>,{,} | Nothing
Encontrar módulo / resto
Encontrar n módulo m es una de las operaciones aritméticas básicas, importante para muchos desafíos. Para los casos m> 0 y n> = 0 , el siguiente fragmento de 46 bytes puede ser utilizado. Se supone que n está en la parte superior de la pila activa con m el siguiente hacia abajo, y los reemplaza con n mod m . Deja el resto de las pilas intactas.
Esta versión anotada muestra el contenido de la pila en algunos puntos del programa.
;
separa las dos pilas y.
marca la pila activa.fuente
{({}[()])<>}
), pero una vez que lo descubrí ... Genio :-)Redundancia Push-Pop
Este es un grande. También es un poco matizado.
La idea es que si presiona algo y luego lo hace estallar sin hacer nada, no debería haberlo presionado.
Por ejemplo, si desea mover algo a la pila y luego agregar uno, puede escribir el siguiente código:
Esto puede ser más simple así:
El primer programa recoge el elemento, lo mueve, lo recoge de nuevo y agrega uno, mientras que el segundo hace las dos cosas de una sola vez.
Ese ejemplo fue simple, sin embargo, puede volverse mucho más complejo. Toma por ejemplo:
La reducción es menos clara aquí, pero el 4º pop en el programa se puede reducir con el 2º empuje, así:
Esta es la herramienta más poderosa en mi repertorio de golf, pero requiere algo de práctica para ser bueno en eso. Una vez que haya estado haciendo esto por un tiempo, podrá detectarlo casi al instante.
fuente
({}<{}>)
?({}<{}>)
destruye el elemento por completo. Sin embargo, estos programas no estaban destinados realmente a ser óptimos solo para resaltar el principio en el trabajo aquí.Optimiza tus enteros
Los enteros son tediosos de representar en Brain-Flak. Afortunadamente tenemos una pregunta que ayudará a Golf a un Brain-Flak Integer para usted. (Tenga en cuenta que la pregunta está diseñada para empujar el entero a la pila, por lo que la redundancia push-pop probablemente se aplica a programas más realistas).
fuente
Empuje contadores de bucle adicionales
Con frecuencia, querrás hacer algo como
o
Cuando la entrada puede contener '0', o el resultado de la operación X puede dar un 0, esto es realmente inconveniente. Porque deberás hacer:
Para hacer X a cada elemento, y luego
Para revertir la matriz de nuevo.
Peor aún, si estamos operando en pares de elementos adyacentes, tendremos que hacerlo
([][()])
en lugar de([])
. Esto es realmente inconveniente. Aquí está el truco: mientras haces X para cada elemento, empuja un 1 sobre la pila alternativa justo encima delX(element)
. Luego, mientras lo inviertes, simplemente puedes hacerEsto es 8 bytes más corto, por lo que cuando factoriza el código adicional para presionar 1, terminará ahorrando 4-6 bytes. Para un ejemplo más concreto, compare la tarea de obtener deltas de una matriz. Sin este truco, necesitarías:
Por 62. Con este truco, tendrás
Para 58. Si se usa de la manera correcta (por ejemplo, retroceder primero y eliminar dos
([][()])
de más adelante), esto podría ahorrar aún más en escenarios específicos.fuente
Aproveche la nilad 'Stack-Height'
Especialmente en los desafíos de complejidad kolmogorov , o desafíos en los que siempre conoce el tamaño de la entrada, puede aprovechar la nilad 'Altura de pila'
[]
para crear enteros.Analicemos esto con un desafío hipotético: la salida
CAT
. La forma que no es de golf es usar el jugador de golf entero en línea para empujar 67, 65 y 84. Esto proporciona:(Nuevas líneas para mayor claridad). Esto es 88 bytes, y no tan bueno. Si, en cambio, empujamos las diferencias consecutivas entre los valores, podemos ahorrar mucho. Entonces envolvemos el primer número en un llamada push y restamos 2:
Luego, tomamos este código, lo envolvemos en un llamada push y agregamos 19 al final:
¡Esto es 62 bytes, para un enorme golf de 26 bytes!
Ahora aquí es donde podemos aprovechar el nilad de altura de pila. Cuando comencemos a presionar 19, sabemos que ya hay 2 elementos en la pila, por
[]
lo que evaluaremos2
. Podemos usar esto para crear un 19 en menos bytes. La forma obvia es cambiar lo interno()()()
a()[]
. Sin embargo, esto solo ahorra dos bytes. Con algunos ajustes más, resulta que podemos empujar 19 conEsto nos ahorra 6 bytes. Ahora tenemos 56.
Puede ver que este consejo se usa de manera muy efectiva en estas respuestas:
Hola mundo v1
Hola mundo v2
Magic The Gathering, amigo o enemigo
Salida El entero perdido
Alfabeto Diagonal (mi favorito personal)
fuente
CAT
programa realmente empujaTAC
: P[]
: anteponer0
s con(<>)
s antes de su código. De todos modos, esto solo funciona si planea empujar el código en reversa de todos modos, pero si encuentra el número correcto, puede reducir su código bastante. Un ejemplo bastante extremo en el que agrego 60
s, que me permite usar tantos[]
s como yo uso()
Usa la wiki
¡Tenemos una wiki ! Tiene algunas deficiencias, pero es un recurso útil. Tiene listas de programas útiles, bien diseñados y apilados que puede pegar en su código. Si quieres hacer algo, crees que alguien podría haberlo hecho antes de que haya una buena posibilidad de que esté en la wiki.
fuente
Mejor bucle
Aquí hay una fácil:
Una construcción bastante común es:
Donde desea recorrer n veces pero aún mantener la n. Sin embargo, esto se puede escribir como:
para guardar 2 bytes.
Otro paradigma bastante común es
Lo que hará un bucle y acumulará toda la pila. Debido a algunas matemáticas elegantes, esto es lo mismo que:
fuente
Revisa tus negativos
A veces puede jugar al golf unos pocos bytes eligiendo estratégicamente qué negar con el
[...]
mónada.Un ejemplo simple está en
[...]
s anidados . Por ejemplo[()()()[()()]]
podría ser[()()()]()()
Supongamos que desea verificar si un valor es uno de los corchetes iniciales
(<{[
. El intento inicial es empujar la diferencia entre cada personaje y recorrerlo para restarloSin embargo, ¡puede ahorrar 4bytes presionando las versiones negativas de las diferencias!
En general, esto no te ahorra mucho, pero también cuesta muy poco esfuerzo cambiar lo que
[...]
está rodeando. Esté atento a las situaciones en las que puede empujar el negativo de un contador en lugar del positivo para ahorrar en incrementos múltiples veces en lugar de disminuir más adelante. O intercambiando(a[b])
con los([a]b)
que hacen la diferencia entre dos números negativos en lugar de positivos.fuente
<a<b>c>
-><abc>
y<a[b]c>
-><abc>
.