El libro "The Pragmatic Programmer" (si no lo has leído, ¡deja de leer este artículo y hazlo ya!) dice que cada año deberíamos aprender un nuevo lenguaje de programación.
Aunque algunos argumenten que es demasiado esfuerzo, todos estaremos de acuerdo en que puede ser una buena idea. Elegir un nuevo idioma para aprender no es tan fácil. No queremos dedicar nuestro tiempo a algo que quizá nunca utilicemos en la práctica, ¿verdad? Pero quizá a veces deberíamos hacer una excepción y aprender algo sólo por diversión. Me gustaría presentarte el lenguaje Brainfuck. Es un lenguaje que puedes aprender en un par de minutos, así que no hay problema de invertir demasiado tiempo en vano. Además, puedo prometerte que resolver cualquier problema con Brainfuck estimulará tu cerebro (todos los f*cks son sólo un extra ;)). Empecemos! Según Wikipedia:
Brainfuck es un lenguaje de programación esotérico creado en 1993 por Urban Müller. El lenguaje consta de sólo ocho comandos sencillos y un puntero de instrucciones. Aunque es totalmente Turing-completo, no está pensado para un uso práctico, sino para desafiar y divertir a los programadores.
Panorama lingüístico
Imagina una cinta infinitamente larga compuesta de celdas, cada una inicializada a 0. También hay un puntero de datos móvil que inicialmente apunta a la primera celda. También hay dos flujos de bytes para entrada y salida. Las instrucciones se ejecutan secuencialmente, una a una. La máquina se detiene después de ejecutar la última.
Comando
¿Qué hace?
>
mover el puntero de datos a la siguiente celda de la derecha
<
mover el puntero de datos a la siguiente celda de la izquierda
+
incrementar el valor de la celda actual
–
disminuir el valor de la celda actual
.
salida del byte de una celda apuntada actualmente en ASCII código
,
leer un byte de stdin y almacenar su valor en la celda actual
[
si la celda actual es 0 entonces salta a la celda coincidente ]
]
saltar a la coincidencia [
Todos los caracteres que no sean ><+-.,[] se ignoran.
Veamos un ejemplo sencillo:
,+.
Se interpretará de la siguiente manera:
leer un byte y almacenarlo en la celda actual (celda0)
incrementar el valor de la celda actual (celda0 = celda0 + 1)
escribir el contenido de la celda actual en la salida
Como resultado, se leerá un carácter de la entrada y se imprimirá el siguiente carácter de la tabla ASCII.
Intérprete / Compilador
Antes de escribir programas útiles (?) en Brainfuck, necesitamos un intérprete o un compilador. AFAIK, no hay uno oficial, pero no es un problema. Hay docenas no oficiales en Internet. Puedo recomendar estos dos:
"¡Hola Mundo!" debería ser el primer programa que escribimos mientras aprendemos un nuevo lenguaje. Sin embargo, escribirlo en Brainfuck es un poco más difícil que en otros lenguajes. Tenemos que empezar con algo más fácil... Escribamos un programa que imprima una sola letra "H" en la pantalla (qué emocionante :D):
¿Cómo funciona? Establece el valor de la celda actual a 72 (realizando incrementos de 72) y lo imprime en la pantalla utilizando "." (H tiene el código 72 en ASCII). Ahora ya sabes lo que debemos hacer para imprimir "¡Hola Mundo!" en la pantalla, pero antes haremos una pequeña refactorización. Escribir todos esos '+' requiere teclear y contar demasiado. Podemos hacerlo más corto usando [ y ] para el bucle. Para establecer el valor en 72 podemos, por ejemplo, hacer un bucle que aumente el valor 7 veces por 10. De esta forma obtenemos 70. Sumando 2 obtendremos 72. Se ve así:
++++++++++ # poner celda0 a 10
[ # bucle hasta que celda0 sea 0
- # disminuir celda0
> # mover el puntero de datos a la derecha (celda1)
+++++++ # aumentar celda1 en 7
# mover el puntero de datos a la derecha (celda1)
++ # aumentar en 2
. # imprime el resultado
He incluido comentarios para que quede claro cómo funciona todo. El mismo programa sin comentarios:
++++++++++[->+++++++++.
¿No es precioso? 🙂 .
Hola a todos.
Volviendo a nuestro programa "¡Hola Mundo! Podríamos establecer el valor de la primera celda en 72 (H) e imprimirlo, establecer el valor de la segunda celda en 101 (e) e imprimirlo, establecer el valor de la tercera celda en 108 e imprimirlo y así sucesivamente. Aquí está la implementación de este algoritmo:
Sí, sólo 1120 bytes para imprimir "¡Hola Mundo!"... ¡Pero podemos hacerlo mejor! En lugar de usar una nueva celda para cada carácter, usemos sólo una. Para imprimir la letra "e" (101) podemos reutilizar el valor de la celda0 (72). Podemos hacer que aumente en uno 29 veces (101 - 72). Y el resultado es el siguiente:
Son sólo 106 bytes e imprime una nueva línea al final. Asombroso.
Invertir una cadena
Ahora estamos listos para escribir algo más desafiante. Escribamos un programa que lea una línea de entrada y la imprima en orden inverso. El primer problema es leer caracteres y detenerse en el nuevo carácter de línea. Recuerda, no hay romper, si u otras declaraciones similares. Tenemos que utilizar [ y ]. Empecemos con un programa que lee todos los caracteres de la entrada y los coloca en celdas sucesivas:
,[>,]
Comienza con la lectura del primer carácter y continuará hasta el último , devoluciones de operaciones 0. Sin embargo, hará un bucle eterno en la implementación que devuelva algo distinto de O para EOF (el lenguaje no especifica este comportamiento). Entonces, ¿cómo podemos detenernos en el carácter de nueva línea? Aquí está el truco:
+[++++++++++>,----------]
Empezamos con la celda 0 a 1 para asegurarnos de que nuestro bucle se ejecuta al menos una vez. En un bucle incrementamos el valor de la celda actual en 10, movemos el puntero de datos a la siguiente celda, leemos un caracter y disminuimos su valor en 10. De esta manera si se lee un nuevo caracter de línea (10 en ASCII), el programa se detendrá en una próxima iteración, de lo contrario su valor será restaurado sumando 10.
Después de ese paso, nuestras celdas tendrán este aspecto:
11 C1 C2 C3 0* 0 0
Cn es el enésimo carácter de la entrada, y * es la posición actual del puntero de datos. Ahora tenemos que empezar a mover el puntero de datos hacia la izquierda, e imprimir todas las celdas hasta llegar al valor 11. Esta es mi interpretación de la tarea:
Cuando me topé con Brainfuck, un lenguaje de programación esotérico, al principio lo descarté como nada más que un truco o una broma. Este lenguaje peculiar y, como muchos pueden argumentar, alucinantemente difícil, me pareció algo sólo para divertirme. Pero con el tiempo, mi percepción de Brainfuck cambió radicalmente.
La naturaleza enigmática de Brainfuck te desafía, empujándote a ampliar tu perspectiva sobre lenguajes de programación. Este lenguaje esotérico permite apreciar la belleza y la necesidad de los lenguajes de alto nivel a los que estamos acostumbrados. Pone de relieve la importancia de las abstracciones, las convenciones de nomenclatura adecuadas y una disposición organizada de la memoria en el ámbito de los lenguajes de programación. Esto es algo que Brainfuck, con su diseño minimalista de sólo ocho sencillos comandos, no ofrece.
Brainfuck es un lenguaje Turing completo que enfatiza aún más la importancia de tener un código fuente claro y coherente. A pesar de ser reconocido como uno de los lenguajes esotéricos más difíciles para escribir programas, irónicamente brilla como el favorito de los principiantes para cualquiera que quiera crear un compilador o un intérprete Brainfuck propio. La razón es la simplicidad de su conjunto de comandos y el hecho de que no requiere un análisis sintáctico complejo.
Crear un programa Brainfuck es único en dos sentidos. En primer lugar, tienes que adaptarte al uso de un único puntero de memoria, lo que te obliga a pensar de forma diferente sobre tu código fuente. Y en segundo lugar, tienes la "opción cero", que es la posibilidad de poner a cero la celda de memoria, una característica poco común en otros lenguajes de programación formales.
En términos de aprendizaje, hay más de lo que parece cuando se trata de Brainfuck. Si se dispone del tiempo suficiente y de la mentalidad adecuada, es posible escribir el mismo programa de múltiples maneras utilizando diferentes códigos de Brainfuck. La última mitad de este viaje consiste en ser inventivo y encontrar formas nuevas y creativas de utilizar sus seis símbolos.
Los intérpretes de Brainfuck, aunque minimalistas, te proporcionan una profunda comprensión de cómo se ejecuta el código, qué imprime el programa y la mecánica subyacente de un lenguaje completo de Turing. En definitiva, Brainfuck no es sólo otro lenguaje de programación esotérico. Es una dimensión completamente nueva, una visión diferente de cómo vemos, entendemos y escribimos programas.