Mini-Flak, 6851113 ciclos
El programa (literalmente)
Sé que la mayoría de las personas probablemente no esperan que una quine Mini-Flak use caracteres no imprimibles e incluso caracteres de varios bytes (lo que hace que la codificación sea relevante). Sin embargo, este quine, y los no imprimibles, combinados con el tamaño del quine (93919 caracteres codificados como 102646 bytes de UTF-8), hacen que sea bastante difícil colocar el programa en esta publicación.
Sin embargo, el programa es muy repetitivo y, como tal, se comprime muy bien. Para que todo el programa esté disponible literalmente de Stack Exchange, hay un xxd
hexdump reversible de una gzip
versión comprimida de la quine completa oculta detrás del colapsable a continuación:
00000000: 1f8b 0808 bea3 045c 0203 7175 696e 652e .......\..quine.
00000010: 6d69 6e69 666c 616b 00ed d9db 6a13 4118 miniflak....j.A.
00000020: 0060 2f8b f808 0d64 a1c1 1dc8 4202 c973 .`/....d....B..s
00000030: 4829 4524 0409 22e2 5529 a194 1242 1129 H)E$..".U)...B.)
00000040: d2d7 ca93 f9cf 4c4c d45b 9536 e6db 6967 ......LL.[.6..ig
00000050: 770e 3bc9 ffed eca9 edb7 b1a4 9ad2 6a1d w.;...........j.
00000060: bfab 75db c6c6 6c5f 3d4f a5a6 8da6 dcd8 ..u...l_=O......
00000070: 465b d4a5 5a28 4bd9 719d 727b aa79 f9c9 F[..Z(K.q.r{.y..
00000080: 43b6 b9d7 8b17 cd45 7f79 d3f4 fb65 7519 C......E.y...eu.
00000090: 59ac 9a65 bfdf 8f86 e6b2 69a2 bc5c 4675 Y..e......i..\Fu
000000a0: d4e4 bcd9 5637 17b9 7099 9b73 7dd3 fcb2 ....V7..p..s}...
000000b0: 4773 b9bc e9bd b9ba 3eed 9df7 aeaf 229d Gs......>.....".
000000c0: e6ed 5eae 3aef 9d46 21b2 5e4d bd28 942e ..^.:..F!.^M.(..
000000d0: 6917 d71f a6bf 348c 819f 6260 dfd9 77fe i.....4...b`..w.
000000e0: df86 3e84 74e4 e19b b70e 9af0 111c fa0d ..>.t...........
000000f0: d29c 75ab 21e3 71d7 77f6 9d8f f902 6db2 ..u.!.q.w.....m.
00000100: b8e1 0adf e9e0 9009 1f81 f011 18d8 1b33 ...............3
00000110: 72af 762e aac2 4760 6003 1bd8 698c c043 r.v...G``...i..C
00000120: 8879 6bde 9245 207c 04ae 5ce6 2d02 e1bb .yk..E |..\.-...
00000130: 7291 4540 57f8 fe0d 6546 f89b a70b 8da9 r.E@W...eF......
00000140: f5e7 03ff 8b8f 3ad6 a367 d60b f980 679d ......:..g....g.
00000150: d3d6 1c16 f2ff a767 e608 57c8 c27d c697 .......g..W..}..
00000160: 4207 c140 9e47 9d57 2e50 6e8e c215 b270 B..@.G.W.Pn....p
00000170: bdf6 9926 9e47 9d05 ce02 0ff0 5ea7 109a ...&.G......^...
00000180: 8ba6 b5db 880b 970b 9749 2864 47d8 1b92 .........I(dG...
00000190: 39e7 9aec 8f0e 9e93 117a 6773 b710 ae53 9........zgs...S
000001a0: cd01 17ee b30e d9c1 15e6 6186 7a5c dc26 ..........a.z\.&
000001b0: 9750 1d51 610a d594 10ea f3be 4b7a 2c37 .P.Qa.......Kz,7
000001c0: 2f85 7a14 8fc4 a696 304d 4bdf c143 8db3 /.z.....0MK..C..
000001d0: d785 8a96 3085 2acc 274a a358 c635 8d37 ....0.*.'J.X.5.7
000001e0: 5f37 0f25 8ff5 6854 4a1f f6ad 1fc7 dbba _7.%..hTJ.......
000001f0: 51ed 517b 8da2 4b34 8d77 e5b2 ec46 7a18 Q.Q{..K4.w...Fz.
00000200: ffe8 3ade 6fed b2f2 99a3 bae3 c949 9ab5 ..:.o........I..
00000210: ab75 d897 d53c b258 a555 1b07 63d6 a679 .u...<.X.U..c..y
00000220: 4a51 5ead a23a 6a72 9eb6 d569 960b f3dc JQ^..:jr...i....
00000230: 9ceb 53fa 658f 345f ad07 6f6f efce 06ef ..S.e.4_..oo....
00000240: 0677 b791 cef2 f620 57bd 1b9c 4521 b241 .w..... W...E!.A
00000250: 4d83 2894 2eaf a140 8102 050a 1428 50a0 M.(....@.....(P.
00000260: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000270: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000280: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000290: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
000002a0: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
000002b0: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
000002c0: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
000002d0: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
000002e0: 0205 0a14 2850 a040 8102 050a 1428 50a0 ....(P.@.....(P.
000002f0: 4081 0205 0a14 2850 a040 8102 050a 1428 @.....(P.@.....(
00000300: 50a0 4081 0205 0a14 2850 a040 8102 050a P.@.....(P.@....
00000310: 1428 50a0 4081 0205 0a14 2850 a040 8102 .(P.@.....(P.@..
00000320: 050a 1428 50a0 4081 0205 0a14 2850 a040 ...(P.@.....(P.@
00000330: 8102 050a 1428 50a0 4081 0205 0a14 2850 .....(P.@.....(P
00000340: a040 8102 050a 1428 50a0 4081 0205 0a14 .@.....(P.@.....
00000350: 2850 a040 8102 050a 1428 50a0 4081 0205 (P.@.....(P.@...
00000360: 0a14 2850 a040 8102 050a 1428 50a0 4081 ..(P.@.....(P.@.
00000370: 0205 0a14 2850 a01c 14ca 7012 cbb4 a6e9 ....(P....p.....
00000380: e6db e6b1 e4b1 9e4c 4ae9 d3be f5f3 745b .......LJ.....t[
00000390: 37a9 3d6a af49 7489 a6e9 ae5c 96dd 488f 7.=j.It....\..H.
000003a0: d31f 5da7 fbad 5d56 3e73 5277 7cf5 aa7b ..]...]V>sRw|..{
000003b0: 3fbc df7c e986 c3ba 5ee4 3c6f 74f7 c3e1 ?..|....^.<ot...
000003c0: 301a bb45 d795 9afb fbdc 1495 65d5 6d9b 0..E........e.m.
000003d0: baf7 a5b4 a87d 4a5b d7fd b667 b788 ec27 .....}J[...g...'
000003e0: c5d8 28bc b96a 9eda 7a50 524d 290a a5cb ..(..j..zPRM)...
000003f0: cbef 38cb c3ad f690 0100 ..8.......
(Sí, es tan repetitivo que incluso puedes ver las repeticiones después de que se ha comprimido).
La pregunta dice: "También recomendaría no ejecutar su programa en TIO. No solo es TIO más lento que el intérprete de escritorio, sino que también expirará en aproximadamente un minuto. Sería extremadamente impresionante si alguien lograra un puntaje lo suficientemente bajo para ejecutar su programa antes de que TIO expire ". ¡Yo puedo hacer eso! Se tarda unos 20 segundos en ejecutarse en TIO, utilizando el intérprete Ruby: ¡ Pruébelo en línea!
El programa (legible)
Ahora que he dado una versión del programa que las computadoras pueden leer, intentemos una versión que los humanos puedan leer. He convertido los bytes que componen la quine en la página de códigos 437 (si tienen el bit alto establecido) o imágenes de control Unicode (si son códigos de control ASCII), agregué espacios en blanco (cualquier espacio en blanco preexistente se convirtió para controlar imágenes) ), codificado en longitud de ejecución utilizando la sintaxis «string×length»
, y algunos bits con gran cantidad de datos elididos:
␠
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
… much more data encoded the same way …
(«()×117»(«()×115»(«()×117»
«000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
(El "casi 241" se debe a que a la copia 241 le falta el final '
, pero por lo demás es idéntico a los otros 240).
Explicación
Sobre los comentarios
Lo primero que hay que explicar es, ¿qué pasa con los caracteres no imprimibles y otra basura que no son comandos de Mini-Flak? Puede pensar que agregar comentarios al quine solo hace las cosas más difíciles, pero esta es una competencia de velocidad (no una competencia de tamaño), lo que significa que los comentarios no perjudican la velocidad del programa. Mientras tanto, Brain-Flak y, por lo tanto, Mini-Flak, simplemente vuelcan el contenido de la pila a la salida estándar; si tuviera que asegurarse de que la pila contenga sololos personajes que componían los comandos de tu programa, tendrías que pasar ciclos limpiando la pila. Tal como está, Brain-Flak ignora a la mayoría de los personajes, por lo que siempre que nos aseguremos de que los elementos de la pila de basura no sean comandos válidos de Brain-Flak (lo que lo convierte en un políglota Brain-Flak / Mini-Flak), y no son negativos o externos. En el rango Unicode, podemos dejarlos en la pila, permitir que se impriman y colocar el mismo carácter en nuestro programa en el mismo lugar para retener la propiedad quine.
Hay una manera particularmente importante en que podemos aprovechar esto. La quine funciona mediante el uso de una cadena de datos larga, y básicamente toda la salida de la quine se produce formateando la cadena de datos de varias maneras. Solo hay una cadena de datos, a pesar de que el programa tiene varias piezas; entonces necesitamos poder usar la misma cadena de datos para imprimir diferentes partes del programa. El truco "los datos basura no importan" nos permite hacer esto de una manera muy simple; almacenamos los caracteres que componen el programa en la cadena de datos agregando o restando un valor ao de su código ASCII. Específicamente, los caracteres que constituyen el inicio del programa se almacenan como su código ASCII + 4, los caracteres que forman la sección que se repite casi 241 veces como su código ASCII - 4,cada carácter de la cadena de datos con un desplazamiento; si, por ejemplo, lo imprimimos con 4 agregados a cada código de caracteres, obtenemos una repetición de la sección repetida, con algunos comentarios antes y después. (Esos comentarios son simplemente las otras secciones del programa, con códigos de caracteres desplazados para que no formen comandos válidos de Brain-Flak, porque se agregó el desplazamiento incorrecto. Tenemos que esquivar los comandos de Brain-Flak, no solo Mini- Comandos antiaéreos, para evitar violar la parte de fuente restringida de la pregunta; la elección de las compensaciones fue diseñada para garantizar esto).
Debido a este truco de comentarios, en realidad solo necesitamos poder generar la cadena de datos formateada de dos maneras diferentes: a) codificada de la misma manera que en la fuente, b) como códigos de caracteres con un desplazamiento especificado agregado a cada código. Esa es una gran simplificación que hace que la longitud agregada valga totalmente la pena.
Estructura del programa
Este programa consta de cuatro partes: la introducción, la cadena de datos, el formateador de la cadena de datos y la salida. La introducción y la salida son básicamente responsables de ejecutar la cadena de datos y su formateador en un bucle, especificando el formato apropiado cada vez (es decir, si codificar o compensar, y qué compensación usar). La cadena de datos es solo datos, y es la única parte de la línea para la cual los caracteres que la componen no se especifican literalmente en la cadena de datos (hacerlo obviamente sería imposible, ya que tendría que ser más largo que sí mismo); Por lo tanto, está escrito de una manera que es particularmente fácil de regenerar a partir de sí mismo. El formateador de la cadena de datos está hecho de 241 partes casi idénticas, cada una de las cuales formatea un dato específico del 241 en la cadena de datos.
Cada parte del programa se puede producir a través de la cadena de datos y su formateador de la siguiente manera:
- Para producir el outro, formatee la cadena de datos con desplazamiento +8
- Para producir el formateador de cadena de datos, formatee la cadena de datos con desplazamiento +4, 241 veces
- Para producir la cadena de datos, formatee la cadena de datos codificándola en el formato fuente
- Para producir la introducción, formatee la cadena de datos con desplazamiento -4
Entonces, todo lo que tenemos que hacer es observar cómo funcionan estas partes del programa.
La cadena de datos
(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …
Necesitamos una codificación simple para la cadena de datos, ya que tenemos que poder invertir la codificación en código Mini-Flak. ¡No puedes ser mucho más simple que esto!
La idea clave detrás de esta quine (aparte del truco de comentarios) es notar que básicamente solo hay un lugar donde podemos almacenar grandes cantidades de datos: las "sumas de valores de retorno de comando" dentro de los diversos niveles de anidamiento de la fuente del programa. (Esto se conoce comúnmente como la tercera pila, aunque Mini-Flak no tiene una segunda pila, por lo que "pila de trabajo" es probablemente un mejor nombre en el contexto de Mini-Flak.) Las otras posibilidades para almacenar datos serían la pila principal / primera (que no funciona porque ahí es donde debe ir nuestra salida, y no podemos mover la salida más allá del almacenamiento de una manera remotamente eficiente), y codificada en un bignum en un solo elemento de pila (que no es adecuado para este problema porque lleva un tiempo exponencial para extraer datos de él); cuando los eliminas, la pila de trabajo es la única ubicación restante.
Para "almacenar" datos en esta pila, utilizamos comandos no balanceados (en este caso, la primera mitad de un (…)
comando), que se equilibrarán más adelante en el formateador de cadenas de datos. Cada vez que cerramos uno de estos comandos dentro del formateador, empujará la suma de un dato tomado de la cadena de datos y los valores de retorno de todos los comandos en ese nivel de anidamiento dentro del formateador; podemos asegurar que este último se agregue a cero, por lo que el formateador simplemente ve valores individuales tomados de la cadena de datos.
El formato es muy simple: (
seguido de n copias de ()
, donde n es el número que queremos almacenar. (Tenga en cuenta que esto significa que solo podemos almacenar números no negativos, y el último elemento de la cadena de datos debe ser positivo).
Un punto poco intuitivo acerca de la cadena de datos es en qué orden se encuentra. El "inicio" de la cadena de datos es el final más cercano al inicio del programa, es decir, el nivel de anidamiento más externo; esta parte se formatea en último lugar (ya que el formateador se ejecuta desde los niveles de anidación más internos a los más externos). Sin embargo, a pesar de estar formateado en último lugar, se imprime primero, porque el intérprete Mini-Flak imprime los valores introducidos en la pila primero. El mismo principio se aplica al programa en su conjunto; primero debemos formatear el outro, luego el formateador de la cadena de datos, luego la cadena de datos, luego la introducción, es decir, el reverso del orden en que están almacenados en el programa.
El formateador de cadena de datos
)[({})(
([({})]({}{}))
{
((()[()]))
}{}
{
{
({}(((({}())[()])))[{}()])
}{}
(({}))
((()[()]))
}{}
)]{}
El formateador de la cadena de datos está formado por 241 secciones que tienen un código idéntico (una sección tiene un comentario marginalmente diferente), cada una de las cuales formatea un carácter específico de la cadena de datos. (No podríamos usar un bucle aquí: necesitamos un desequilibrado )
para leer la cadena de datos haciendo coincidir su desequilibrado (
, y no podemos poner uno de esos dentro de un {…}
bucle, la única forma de bucle que existe. Entonces, en su lugar, " desenrolle "el formateador, y simplemente haga que la introducción / outro emita la cadena de datos con el desplazamiento del formateador 241 veces).
)[({})( … )]{}
La parte más externa de un elemento formateador lee un elemento de la cadena de datos; La simplicidad de la codificación de la cadena de datos conduce a una pequeña complejidad al leerla. Comenzamos cerrando lo que no coincide (…)
en la cadena de datos, luego negamos ( […]
) dos valores: el dato que acabamos de leer de la cadena de datos ( ({})
) y el valor de retorno del resto del programa. Copiamos el valor de retorno del resto del elemento formateador con (…)
y agregamos la copia a la versión negada con {}
. El resultado final es que el valor de retorno del elemento de cadena de datos y el elemento formateador juntos es el dato menos el dato menos el valor de retorno más el valor de retorno, o 0; Esto es necesario para que el siguiente elemento de cadena de datos produzca el valor correcto.
([({})]({}{}))
El formateador utiliza el elemento de la pila superior para saber en qué modo está (0 = formato en formato de cadena de datos, cualquier otro valor = el desplazamiento con el que se generará). Sin embargo, solo después de leer la cadena de datos, el dato está en la parte superior del formato en la pila, y los queremos al revés. Este código es una variante más corta del código de intercambio Brain-Flak, tomando a arriba b a b arriba a + b ; no solo es más corto, también es (en este caso específico) más útil, porque el efecto secundario de agregar b a a no es problemático cuando b es 0, y cuando b no es 0, hace el cálculo de compensación para nosotros.
{
((()[()]))
}{}
{
…
((()[()]))
}{}
Brain-Flak solo tiene una estructura de flujo de control, por lo que si queremos algo más que un while
bucle, tomará un poco de trabajo. Esta es una estructura "negativa"; si hay un 0 en la parte superior de la pila, lo elimina, de lo contrario, coloca un 0 en la parte superior de la pila. (Funciona de manera bastante simple: siempre que no haya un 0 en la parte superior de la pila, empuje 1 - 1 a la pila dos veces; cuando haya terminado, haga estallar el elemento de la pila superior).
Es posible colocar código dentro de una estructura negada, como se ve aquí. El código solo se ejecutará si la parte superior de la pila no es cero; así que si tenemos dos estructuras negadas, suponiendo que los dos elementos superiores de la pila no sean ambos cero, se cancelarán entre sí, pero cualquier código dentro de la primera estructura se ejecutará solo si el elemento superior de la pila no es cero, y el código dentro la segunda estructura se ejecutará solo si el elemento de la pila superior era cero. En otras palabras, esto es el equivalente de una declaración if-then-else.
En la cláusula "entonces", que se ejecuta si el formato no es cero, en realidad no tenemos nada que hacer; lo que queremos es empujar los datos + desplazamiento a la pila principal (para que pueda salir al final del programa), pero ya está allí. Entonces, solo tenemos que lidiar con el caso de codificar el elemento de cadena de datos en forma de origen.
{
({}(((({}())[()])))[{}()])
}{}
(({}))
Así es como lo hacemos. La {({}( … )[{}()])}{}
estructura debe ser familiar como un bucle con un número específico de iteraciones (que funciona moviendo el contador del bucle a la pila de trabajo y manteniéndolo allí; estará a salvo de cualquier otro código, porque el acceso a la pila de trabajo está vinculado a el nivel de anidamiento del programa). El cuerpo del bucle es ((({}())[()]))
, que hace tres copias del elemento de la pila superior y agrega 1 al más bajo. En otras palabras, transforma un 40 en la parte superior de la pila en 40 por encima de 40 por encima de 41, o visto como ASCII, (
en (()
; la ejecución de este repetidamente hará (
en (()
en (()()
en (()()()
y así sucesivamente, y por lo tanto es una forma sencilla de generar nuestra cadena de datos (suponiendo que hay una (
en la parte superior de la pila ya).
Una vez que hayamos terminado con el bucle, (({}))
duplica la parte superior de la pila (de modo que ahora comience en ((()…
lugar de (()…
. La (
siguiente copia del formateador de cadena de datos usará el encabezado para formatear el siguiente carácter (lo expandirá en (()(()…
luego (()()(()…
, y así sucesivamente, entonces esto genera la separación (
en la cadena de datos).
%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'
Hay un último interés en el formateador de cadena de datos. OK, así que esto es solo el outro desplazado 4 puntos de código hacia abajo; sin embargo, ese apóstrofe al final puede parecer fuera de lugar. '
(punto de código 39) cambiaría a +
(punto de código 43), que no es un comando Brain-Flak, por lo que puede haber adivinado que está ahí para algún otro propósito.
La razón por la que esto está aquí es porque el formateador de la cadena de datos espera que ya haya un (
en la pila (no contiene un 40 literal en ninguna parte). los'
en realidad se encuentra al comienzo del bloque que se repite para formar el formateador de la cadena de datos, no al final, por lo que después de que los caracteres del formateador de la cadena de datos se hayan introducido en la pila (y el código está a punto de pasar a imprimir la cadena de datos ), el outro ajusta el 39 en la parte superior de la pila en un 40, listo para que el formateador (el formateador en sí mismo esta vez, no su representación en la fuente) lo use. Es por eso que tenemos "casi 241" copias del formateador; a la primera copia le falta su primer carácter. Y ese carácter, el apóstrofe, es uno de los únicos tres caracteres en la cadena de datos que no corresponde al código Mini-Flak en algún lugar del programa; está ahí simplemente como un método para proporcionar una constante.
La introducción y outro
(((()()()()){}))
{{}
(({})[(()()()())])
(({})(
{{}{}((()[()]))}{}
(((((((({})){}){}{})){}{}){}){}())
{
({}(
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
…
)[{}()])
}{}
{}({}())
)[{}])
(({})(()()()()){})
}{}{}␊
La introducción y la salida son conceptualmente la misma parte del programa; La única razón por la que hacemos una distinción es que el outro debe imprimirse antes que la cadena de datos y su formateador (para que se imprima después de ellos), mientras que la introducción debe imprimirse después de ellos (imprimir antes de ellos).
(((()()()()){}))
Comenzamos colocando dos copias de 8 en la pila. Este es el desplazamiento para la primera iteración. La segunda copia se debe a que el bucle principal espera que haya un elemento basura en la parte superior de la pila sobre el desplazamiento, dejado atrás de la prueba que decide si existe el bucle principal, por lo que necesitamos colocar un elemento basura allí para que no tira el elemento que realmente queremos; una copia es la forma más segura (por lo tanto, la más rápida de generar) para hacerlo.
Hay otras representaciones del número 8 que no son más largas que esta. Sin embargo, cuando se busca el código más rápido, esta es definitivamente la mejor opción. Por un lado, usar ()()()()
es más rápido que, digamos, (()()){}
porque a pesar de que ambos tienen 8 caracteres de longitud, el primero es un ciclo más rápido, porque (…)
se cuenta como 2 ciclos, pero ()
solo como uno. Sin embargo, guardar un ciclo es insignificante en comparación con una consideración mucho mayor para una quine : (
y )
tiene puntos de código mucho más bajos que {
y }
, por lo que generar el fragmento de datos para ellos será mucho más rápido (y el fragmento de datos ocupará menos espacio en el código, también).
{{} … }{}{}
El bucle principal. Esto no cuenta las iteraciones (es un while
bucle, no un for
bucle, y utiliza una prueba para salir). Una vez que sale, descartamos los dos elementos superiores de la pila; el elemento superior es un 0 inofensivo, pero el elemento a continuación será el "formato para usar en la próxima iteración", que (siendo un desplazamiento negativo) es un número negativo, y si hay números negativos en la pila cuando el Mini -El programa Flak sale, el intérprete falla al intentar generarlos.
Debido a que este ciclo utiliza una prueba explícita para salir, el resultado de esa prueba se dejará en la pila, por lo que lo descartamos como lo primero que hacemos (su valor no es útil).
(({})[(()()()())])
Este código empuja 4 y f - 4 sobre un elemento de pila f , mientras deja ese elemento en su lugar. Estamos calculando el formato para la próxima iteración por adelantado (mientras tenemos las 4 constantes a la mano), y simultáneamente colocamos la pila en el orden correcto para las siguientes partes del programa: usaremos f como formato para esta iteración, y el 4 es necesario antes de eso.
(({})( … )[{}])
Esto guarda una copia de f - 4 en la pila de trabajo, para que podamos usarlo en la próxima iteración. (El valor de f todavía estará presente en ese punto, pero estará en un lugar incómodo en la pila, e incluso si pudiéramos maniobrarlo al lugar correcto, tendríamos que pasar ciclos restando 4 de él, y ciclos de impresión del código para hacer esa resta. Mucho más fácil simplemente almacenarlo ahora).
{{}{}((()[()]))}{}
Una prueba para ver si el desplazamiento es 4 (es decir, f - 4 es 0). Si es así, estamos imprimiendo el formateador de la cadena de datos, por lo que debemos ejecutar la cadena de datos y su formateador 241 veces en lugar de solo una vez en este desplazamiento. El código es bastante simple: si f - 4 no es cero, reemplace el f - 4 y el 4 en sí mismo con un par de ceros; luego, en cualquier caso, haga estallar el elemento de la pila superior. Ahora tenemos un número sobre f en la pila, ya sea 4 (si queremos imprimir esta iteración 241 veces) o 0 (si queremos imprimirlo solo una vez).
(
((((((({})){}){}{})){}{}){}){}
()
)
Este es un tipo interesante de constante Brain-Flak / Mini-Flak; la línea larga aquí representa el número 60. Puede estar confundido por la falta de ()
, que normalmente están por todas partes en las constantes de Brain-Flak; Este no es un número regular, sino un número de la Iglesia, que interpreta los números como una operación de duplicación. Por ejemplo, el número de la Iglesia para 60, visto aquí, hace 60 copias de su entrada y las combina todas en un solo valor; en Brain-Flak, lo único que podemos combinar son números regulares, por suma, por lo que terminamos agregando 60 copias de la parte superior de la pila y multiplicando así la parte superior de la pila por 60.
Como nota al margen, puede usar un buscador de números de Subcarga , que genera números de Iglesia en la sintaxis de Subcarga, para encontrar el número apropiado en Mini-Flak también. Los números de baja carga (que no sean cero) usan las operaciones "duplicar el elemento superior de la pila" :
y "combinar los dos elementos superiores de la pila" *
; ambos existen esas operaciones en Brain-Flak, por lo que sólo traduce :
a )
, *
a {}
, anteponga una {}
, y añadir una cantidad suficiente (
al inicio de equilibrio (esto es usar una mezcla extraña de la chimenea principal y la pila de trabajo, pero funciona).
Este fragmento de código en particular usa el número de iglesia 60 (efectivamente un fragmento "multiplicar por 60"), junto con un incremento, para generar la expresión 60 x + 1. Entonces, si obtuvimos un 4 del paso anterior, esto nos da un valor de 241, o si tuviéramos un 0, solo obtenemos un valor de 1, es decir, esto calcula correctamente el número de iteraciones que necesitamos.
La elección de 241 no es casual; fue un valor elegido para ser a) aproximadamente la longitud a la que el programa terminaría de todos modos yb) 1 más de 4 veces un número redondo. Los números redondos, 60 en este caso, tienden a tener representaciones más cortas como números de la Iglesia porque tiene más flexibilidad en los factores para copiar. El programa contiene relleno más adelante para aumentar exactamente la longitud hasta 241.
{
({}(
…
)[{}()])
}{}
Este es un bucle for, como el visto anteriormente, que simplemente ejecuta el código dentro de él varias veces igual a la parte superior de la pila principal (que consume; el contador del bucle se almacena en la pila de trabajo, pero la visibilidad de eso está relacionado con el nivel de anidación del programa y, por lo tanto, es imposible que otra cosa que el bucle for mismo interactúe con él). En realidad, esto ejecuta la cadena de datos y su formateador 1 o 241 veces, y como ahora hemos extraído todos los valores que estábamos usando para nuestro cálculo de flujo de control desde la pila principal, tenemos el formato para usar encima, listo para El formateador a utilizar.
(␀␀!S␠su! … many more comment characters … oq␝qoqoq)
El comentario aquí no está completamente sin interés. Por un lado, hay un par de comandos Brain-Flak; la )
al final se genera de forma natural como un efecto secundario de la forma en que las transiciones entre los diversos segmentos de la obra programa, por lo que el (
al comienzo se añadió manualmente para equilibrar (y a pesar de la longitud de la comentario dentro, poniendo un comentario dentro de un ()
comando sigue siendo un ()
comando, por lo que todo lo que hace es agregar 1 al valor de retorno de la cadena de datos y su formateador, algo que el bucle for ignora por completo).
Más notablemente, esos caracteres NUL al comienzo del comentario claramente no son compensaciones de nada (incluso la diferencia entre +8 y -4 no es suficiente para convertir un (
a NUL). Esos son rellenos puros para llevar la cadena de datos de 239 elementos a 241 elementos (que se amortizan fácilmente: tomaría mucho más de dos bytes generar 1 frente a 239 en lugar de 1 frente a 241 al calcular el número de iteraciones requeridas ) Se usó NUL como el carácter de relleno porque tiene el punto de código más bajo posible (haciendo que el código fuente de la cadena de datos sea más corto y, por lo tanto, más rápido de salida).
{}({}())
Suelte el elemento de la pila superior (el formato que estamos usando), agregue 1 al siguiente (el último carácter que se generará, es decir, el primer carácter que se imprimirá, de la sección del programa que acabamos de formatear). Ya no necesitamos el formato anterior (el nuevo formato se esconde en la pila de trabajo); y el incremento es inofensivo en la mayoría de los casos, y cambia la '
representación de origen del formateador de cadena de datos en un (
(que se requiere en la pila para la próxima vez que ejecutemos el formateador, para formatear la cadena de datos en sí). Necesitamos una transformación como esa en el outro o intro, porque forzar cada elemento del formateador de cadena de datos para comenzar (
lo haría un poco más complejo (ya que necesitaríamos cerrar (
y luego deshacer su efecto), yde alguna manera necesitaríamos generar un extra en (
algún lugar porque solo tenemos casi 241 copias del formateador, no todas 241 (por lo que es mejor que un personaje inofensivo como '
el que falta).
(({})(()()()()){})
Finalmente, la prueba de salida del bucle. La parte superior actual de la pila principal es el formato que necesitamos para la próxima iteración (que acaba de salir de la pila de trabajo). Esto lo copia y agrega 8 a la copia; el valor resultante se descartará la próxima vez alrededor del ciclo. Sin embargo, si acabamos de imprimir la introducción, el desplazamiento fue -4, por lo que el desplazamiento para la "próxima iteración" será -8; -8 + 8 es 0, por lo que el ciclo saldrá en lugar de continuar en la iteración después.