Itinerarios / básico

Las instrucciones que entiende una máquina

Programar desde el bit, sin saber nada antes: ocho piezas con las que está hecho cualquier programa, cada una ejecutándose a la vista en la página.

8 capítulos · 6,060 palabras · 31 min · cada capítulo es un artículo de la enciclopedia

Este es el itinerario más básico del sitio sobre informática: no supone nada, ni siquiera haber visto código. Ocho capítulos bastan porque un programa cualquiera (una hoja de cálculo, un videojuego, el buscador) está hecho de ocho piezas y ninguna más: los bits en que se representa todo, las instrucciones literales que la máquina sigue, las cajas con nombre que recuerdan valores, el bucle que repite, la condición que decide, la función que da nombre a una regla, el error que hay que encontrar y la traducción que convierte el texto en ejecución.

Cada pieza viene con una figura que la ejecuta en la página, no que la dibuja. El byte se cuenta bit a bit y se desborda en 255; el robot de la rejilla choca contra el muro que su programa no veía; la memoria tacha los valores viejos a cada paso; el bucle planta farolas y deja un tramo a oscuras cuando cuenta mal; la pila de la máquina explica por qué 3 + 4 × 5 es 23 y (3 + 4) × 5 es 35. Leer el itinerario es manejarlo: cada figura se recorre pulsando y ninguna necesita instalar nada.

El orden es el argumento y conviene seguirlo: cada capítulo usa las piezas del anterior y prepara la siguiente. Al final, la continuación natural es «Estructuras de datos que se ven», que empieza donde este termina: sabiendo ya cómo ejecuta la máquina, mide lo que cuesta organizar los datos.

Capítulo 1 de 8

Los bits

Informática 889 palabras artículo suelto ↗

El bit es la unidad mínima de información: la respuesta a una única pregunta de sí o no. Cara o cruz, encendido o apagado, verdadero o falso, uno o cero: todo eso es un bit. El nombre es la contracción de binary digit, dígito binario, y la contracción es justa, porque un bit no es otra cosa que una cifra en un sistema que solo tiene dos. Parece poco, y lo es: un bit apenas distingue entre dos opciones. Pero con ocho bits se distingue entre 256 opciones, con veinte entre un millón y con los que caben en un teléfono, entre más posibilidades que átomos hay en la Tierra. Toda la información digital (textos, fotos, voces, este artículo) es, en el fondo, una sucesión de respuestas de sí o no.

Que las máquinas usen dos estados y no diez no es una decisión matemática, sino de taller. Sería posible construir un dígito decimal con diez niveles de tensión distintos, pero distinguir con fiabilidad diez niveles en un circuito que se calienta, envejece y recibe interferencias es carísimo; distinguir dos (pasa corriente o no pasa) es trivial y deja un margen enorme para el ruido. Un interruptor es el componente electrónico más fácil de fabricar, y la historia del hardware es la de interruptores cada vez más pequeños y rápidos: relés, válvulas de vacío, transistores, miles de millones de transistores. La máquina habla en binario porque el binario es el idioma que sale gratis cuando lo que se sabe construir son interruptores.

Con dos cifras se cuentan todos los números usando el mismo truco posicional del sistema decimal. En decimal, cada posición vale diez veces la de su derecha; en binario, vale el doble. El número 1101 se lee como en el colegio: un ocho, un cuatro, cero doses y un uno; trece. Ocho bits forman un byte, y un byte recorre 256 valores, del 0 al 255. Contar en binario tiene una particularidad visible: como solo hay dos cifras, el acarreo (ese «me llevo una» de la suma) salta constantemente, y al llegar a 255 y sumar uno ocurre algo instructivo: el resultado, 256, necesita nueve bits, no hay novena celda donde guardarlo y el byte se queda en cero, como el cuentakilómetros que da la vuelta. Es el desbordamiento, la primera forma que adopta el infinito dentro de una máquina finita.

La figura muestra un byte entero: ocho celdas, cada una con su peso encima (128, 64, 32, 16, 8, 4, 2, 1) y su cifra dentro. Pulsa cualquier celda para cambiarla entre cero y uno; debajo, el valor se recalcula al instante, desglosado como la suma de los pesos de las celdas encendidas. El botón «Suma uno» añade uno al byte y anima la cascada del acarreo, iluminando cada bit en el momento en que cambia; «Al azar» pone un byte cualquiera. El selector «Leer como» cambia de convenio: los mismos ocho bits, leídos como número o leídos como letra según la tabla ASCII. Lleva el byte a 255, pulsa «Suma uno» y mira lo que pasa.

Interactivo Ocho bits que puedes cambiar con un clic. Suma uno hasta el desbordamiento, pon un byte al azar y cambia el selector para leer los mismos bits como número o como letra ASCII.

El selector de la figura es la parte que importa. Con las celdas en el patrón 01000001, el byte vale 65 si el convenio es numérico y es la letra «A» si el convenio es ASCII, la tabla que en 1963 asignó un número a cada letra del alfabeto inglés. Los bits no cambian; cambia el acuerdo sobre lo que significan. Todo lo que una máquina guarda funciona así: una foto es un convenio que dice que cada tres bytes son las cantidades de rojo, verde y azul de un punto; una canción, un convenio que dice que cada dos bytes miden la presión del aire en un instante; este párrafo, un convenio que dice qué número es cada letra. El bit no porta significado: se lo da quien lo interpreta, y por eso un mismo archivo puede ser una imagen para un programa y un error para otro.

La aritmética binaria es mucho más vieja que el ordenador. La publicó Gottfried Wilhelm Leibniz en 1703, en su Explication de l’arithmétique binaire, presentada ante la Academia de Ciencias de París con más entusiasmo teológico que práctico (le fascinaba que todo naciera del cero y el uno, como la creación de la nada) y sin ninguna aplicación a la vista. La aplicación llegó dos siglos y medio después, en la tesis de máster que Claude Shannon defendió en el MIT en 1937: allí demostró que el álgebra de Boole, la lógica del verdadero y el falso, describe exactamente los circuitos de relés, y que por tanto se podía calcular con interruptores. Esa tesis, que se ha llamado la más importante del siglo, es el puente entre el sí/no matemático y el sí/no eléctrico. Once años después, en A Mathematical Theory of Communication, Shannon fundó la teoría de la información y bautizó a su unidad: escribió que la palabra bit se la había sugerido John Tukey, y quedó.

Un bit, eso sí, no hace nada: solo está. Para que la información sirva de algo tiene que pasarle algo (sumarse, compararse, moverse), y eso lo hacen unas listas de órdenes de una literalidad absoluta. De esas listas, de cómo se escriben y de lo que pasa cuando quien las ejecuta no entiende nada, va el próximo capítulo: el algoritmo.

Capítulo 2 de 8

El algoritmo

Informática 840 palabras artículo suelto ↗

Un algoritmo es una lista finita de instrucciones sin ambigüedad que un ejecutor puede seguir paso a paso, sin entenderlas, para transformar una entrada en una salida. La palabra es un apellido deformado: Muhammad ibn Musa al-Juarismi, matemático de la Bagdad del siglo IX, escribió hacia el año 825 un tratado de procedimientos de cálculo (del ŷabr de su título viene «álgebra») y cuando sus textos se tradujeron al latín, su nombre se convirtió en Algoritmi y las frases «dixit Algoritmi», «dijo Algoritmi», acabaron dando nombre a todo procedimiento mecánico de cálculo.

La propiedad que define a un algoritmo es la literalidad: el ejecutor hace exactamente lo que está escrito, nunca lo que se quiso decir. Esa es a la vez su virtud y su defecto. Virtud, porque un procedimiento literal lo puede ejecutar cualquiera (o cualquier cosa) sin talento ni criterio, y el resultado es siempre el mismo. Defecto, porque si la lista dice una tontería, la tontería se ejecuta con la misma fidelidad. Los programas no fallan por desobediencia; fallan por obediencia. Todo bug de la historia es una máquina haciendo con exactitud lo que se le ordenó y un humano descubriendo que no era lo que quería ordenar.

La figura pone un ejecutor literal en escena: un robot triangular sobre una cuadrícula de siete por cinco, con una casilla marcada como meta. El robot entiende dos órdenes (AVANZA, una casilla en la dirección en que mira, y GIRA, un cuarto de vuelta a la izquierda) y una construcción, REPITE n […], que ejecuta su contenido n veces. El selector ofrece tres programas, escritos a la derecha como una lista numerada: uno llega a la meta, otro choca con un muro y otro dibuja un cuadrado. Con «Un paso» se ejecuta una sola instrucción; con «Ejecutar» (que al pulsarlo pasa a ser «Pausa»), el programa entero a ritmo visible; «Reiniciar» devuelve el robot a la salida. Cada instrucción se ilumina en la lista en el momento de ejecutarse, y la lectura de abajo cuenta cuántas van, hacia dónde mira el robot y en qué casilla está.

Interactivo Elige un programa y ejecútalo paso a paso. Fíjate en la lista numerada: cada instrucción se ilumina al cumplirse, incluida la que estrella al robot contra el borde de la cuadrícula.

Los tres programas enseñan lo mismo desde ángulos distintos. El primero llega a la meta sin que ninguna instrucción mencione la meta: ocho órdenes ciegas (cuatro avances, un giro y tres avances) componen una ruta; el robot no sabía adónde iba en ningún momento, y llegó. El segundo es la lección importante: la séptima instrucción dice AVANZA y el robot avanza, aunque frente a él no haya más cuadrícula; el programa se cumplió al pie de la letra y al muro no le importa lo que se quiso decir. El tercero muestra lo que es un bucle de verdad: REPITE 4 no significa «haz algo parecido cuatro veces», sino que el cuerpo (avanzar, avanzar, girar) se ejecuta literalmente cuatro veces, con lo que el robot traza un cuadrado y termina exactamente donde empezó, mirando exactamente adonde miraba.

Que una lista de instrucciones sea un algoritmo exige varias condiciones que Donald Knuth fijó en 1968, en el primer volumen de The Art of Computer Programming: ha de ser finita (termina), definida (cada paso tiene un único significado), efectiva (cada paso lo puede ejecutar el ejecutor con lápiz, papel o interruptores), y ha de recibir entradas y producir salidas. La condición de «definida» es la que separa los algoritmos de las recetas: «añade sal al gusto» no es una instrucción para un ejecutor literal; «añade cinco gramos de sal» sí. Con un ejecutor que no interpreta, la ambigüedad no se disimula: o muere al escribir el programa o mata al ejecutarlo.

La idea de algoritmo es tan vieja como la aritmética, pero dos textos la convirtieron en la base de la informática. En 1843, traduciendo un artículo sobre la máquina analítica de Charles Babbage, Ada Lovelace añadió unas notas que triplicaban el original: en la nota G escribió, paso a paso, cómo calcularía la máquina los números de Bernoulli (el primer algoritmo publicado pensado para ser ejecutado por una máquina) y vio lo que casi nadie veía, que un artilugio que opera con números según reglas puede operar con cualquier cosa que se represente con símbolos. En 1936 Alan Turing llevó la idea al límite en On Computable Numbers: definió una máquina imaginaria que solo sabe leer una casilla de una cinta, escribirla y moverse, y demostró que ese aparato miserable puede ejecutar cualquier algoritmo. Todo ordenador actual es, en esencia, el robot de la figura: un ejecutor literal con dos o tres órdenes y ninguna comprensión.

Eso sí, fíjate en lo que pasa al pulsar «Reiniciar»: el robot vuelve a la salida como si nada hubiera pasado, y entre una ejecución y la siguiente no queda rastro de lo recorrido, porque un programa así de rígido no recuerda nada. Ni siquiera durante la ejecución guarda el robot más que su posición y su rumbo. De dónde saca una máquina la memoria (cómo un puñado de celdas con nombre convierte un trámite ciego en un programa que acumula) va el próximo capítulo: la variable.

Capítulo 3 de 8

La variable

Informática 740 palabras artículo suelto ↗

Una variable es un nombre atado a una caja cuyo contenido puede cambiar mientras el nombre se queda quieto. Eso es todo, y cuesta más de lo que parece, porque choca con algo aprendido en la escuela: en matemáticas, x = x + 1 es una falsedad, una ecuación sin solución; en un programa, total = total + 1 es una orden rutinaria que significa «saca lo que hay en la caja total, súmale uno y guarda el resultado en la misma caja». El signo igual no afirma que dos cosas sean lo mismo: manda mover un valor de un sitio a otro. Quien lo lee como una ecuación entiende mal cada línea que lee; quien lo lee como una orden ya sabe la mitad de lo necesario para leer programas.

La figura de esta página dibuja ese modelo de cajas. A la izquierda hay un programa pequeño, numerado línea a línea; a la derecha, la memoria: una caja rotulada por cada nombre que el programa usa, con su valor actual y, cuando algo lo sustituye, el valor anterior tachado al lado. El desplegable ofrece cuatro programas: calcular un precio con IVA, contar y sumar «a mano» repitiendo líneas, y dos maneras de intercambiar el contenido de dos cajas, una con una tercera caja auxiliar y otra ingenua que pierde un valor sin remedio. El botón «Un paso» ejecuta la línea resaltada y actualiza la memoria; «Ejecutar» encadena los pasos hasta el final (y mientras tanto se llama «Pausa»); «Reiniciar» vacía las cajas y devuelve el cursor a la primera línea. Debajo, un texto cuenta cada paso con sus números: «total pasa de 14,52 a 15,52».

Interactivo Elige un programa y ejecútalo con «Un paso»: mira cómo nacen las cajas, cómo se tachan los valores viejos y qué ocurre en el intercambio sin auxiliar.

Hacer esto con lápiz y papel se llama trazar un programa, y es el oficio básico de quien programa. Leer código no es leerlo como se lee un párrafo, de principio a fin captando la idea general; es simularlo, llevar en la cabeza el conjunto de cajas y su contenido en cada instante. Ese conjunto tiene nombre propio: el estado. Un programa es, visto así, una máquina de transformar estados: cada línea toma el estado anterior y produce el siguiente, y el programa entero es la cadena de todas esas transformaciones. Edsger Dijkstra construyó sobre esto una disciplina completa en A Discipline of Programming (1976): razonar sobre un programa es razonar sobre los estados por los que puede pasar, y la dificultad de programar consiste en que hay que preverlos todos sin ejecutar ninguno.

El tercer y el cuarto programa del desplegable muestran que las cajas no son una metáfora decorativa. Intercambiar el contenido de a y b parece trivial:

a = b
b = a

Pero la primera orden copia el valor de b en la caja a y destruye lo que había en ella; cuando llega la segunda, el valor original de a ya no existe en ninguna parte y ambas cajas guardan lo mismo. La solución es material: hace falta una tercera caja, aux, que custodie el valor de a mientras dura la mudanza:

aux = a
a = b
b = aux

Es el primer problema de muchos cursos de programación, y se resuelve mal exactamente en la medida en que uno no cree de verdad en las cajas.

El modelo tiene dirección y fecha. En el First Draft of a Report on the EDVAC (1945), John von Neumann describió una máquina cuyo programa viviría en la misma memoria que sus datos, una memoria organizada como celdas numeradas que se pueden escribir y releer: las cajas de la figura, con dirección postal incluida. Cuando Maurice Wilkes puso en marcha el EDSAC en Cambridge, en 1949, aquella arquitectura funcionó por primera vez, y el manual que publicó con Wheeler y Gill en 1951 enseñaba a programar exactamente así: moviendo valores entre celdas. La variable con nombre llegó con los lenguajes. En FORTRAN (1957), John Backus y su equipo hicieron de la asignación la frase central del lenguaje, y el nombre dejó de ser una dirección numérica para convertirse en una palabra elegida por el programador. La caja siguió siendo la misma; lo que cambió fue la etiqueta.

Queda una incomodidad deliberada en el segundo programa del desplegable. Sumar 1 + 2 + 3 exige escribir tres veces la misma línea con números distintos, y sumar hasta cien exigiría escribirla cien veces. Escribir la misma orden cinco veces seguidas es la señal de que la máquina debería repetirla ella sola, cambiando en cada vuelta lo que cambia. De eso trata el siguiente capítulo: el bucle.

Capítulo 4 de 8

El bucle

Informática 736 palabras artículo suelto ↗

Un bucle es la instrucción que ordena a la máquina repetir un trozo de programa cambiando en cada vuelta lo que cambia. Toda repetición útil tiene tres partes: una preparación, que pone el contador en su primer valor; una condición, que decide si toca otra vuelta o ya no; y un paso, que mueve el contador antes de volver a empezar. Lo que hace útil a la repetición es precisamente ese contador: la vuelta número i dispone del número i y puede hacer con él algo que las vueltas anteriores no hicieron, plantar la flor en la posición i, sumar el número i, mirar la casilla i. Repetir sin ese índice sería hacer muchas veces exactamente lo mismo, que es lo que hace una máquina de coser, no lo que hace un programa.

El programa de la figura cabe en tres líneas:

para i desde 1 hasta n
    planta una flor en la posición i
fin para

El deslizador fija n entre 1 y 12. El botón «Un paso» ejecuta una vuelta: resalta la línea que toca, dibuja la flor i-ésima en su posición y actualiza las cuentas; «Ejecutar» encadena las vueltas hasta terminar (y mientras corre se llama «Pausa»). Debajo del listado, dos barras mantienen la comparación que importa: «líneas del bucle: 3» frente a «líneas a mano: n», y la segunda crece cada vez que se mueve el deslizador. La lectura final lo resume: «12 flores con 3 líneas; escritas a mano serían 12 líneas».

Interactivo Sube n con el deslizador y pulsa «Ejecutar»: las flores crecen, las tres líneas no. Compara las dos barras de debajo del listado.

Aquí está el salto que convierte una lista de órdenes en otra cosa. Un programa sin bucles de cien líneas hace como mucho cien cosas; el de la figura, con tres, hace n cosas, y n puede ser doce hoy y un millón mañana sin que nadie toque el código. El programa deja de crecer cuando crece el trabajo. Más aún: n puede no conocerse al escribir, porque lo trae un fichero, lo teclea un usuario o lo mide un sensor, y el mismo texto sirve para todos los casos. Ese es el punto exacto en que un programa deja de ser una lista y empieza a ser una máquina: una pieza fija que produce resultados distintos según lo que se le eche.

La idea es más vieja que los ordenadores. En 1843, traduciendo un artículo de Menabrea sobre la Máquina Analítica de Babbage, Ada Lovelace añadió unas notas que triplicaban el texto original; en la nota G organizó el cálculo de los números de Bernoulli como una operación que se repite con índices que cambian, y explicó que la máquina haría retroceder su juego de tarjetas perforadas para ejecutar de nuevo la misma secuencia: un bucle de cartón, un siglo antes de que existiera algo capaz de ejecutarlo. Cuando por fin hubo máquinas, la repetición tardó poco en vestirse de instrucción: FORTRAN, en 1957, la llamó DO y fijó la forma canónica (preparación, condición, paso) que casi todos los lenguajes posteriores copiaron con otro nombre. Donald Knuth, en el primer volumen de The Art of Computer Programming (1968), dio a los bucles su herramienta de razonamiento, el invariante: aquello que no cambia en ninguna vuelta y que permite demostrar qué habrá cambiado al final, idea que Dijkstra convirtió después en el centro de su método.

El bucle tiene una enemiga doméstica: la vuelta de más o de menos. ¿Hasta n o hasta n − 1? ¿Empezando en 0 o en 1? La diferencia es una sola vuelta y, sin embargo, es la diferencia entre doce flores y once, entre sumar bien y sumar mal. Es el error más común de la programación real, tan común que tiene nombre propio en inglés (off by one) y se merece un examen detenido que llegará en el capítulo dedicado a la depuración. Baste aquí la advertencia: al escribir un bucle, la pregunta no es «¿cuántas veces quiero repetir?» sino «¿qué vale i en la primera vuelta y qué vale en la última?», y las dos respuestas conviene comprobarlas con las manos antes de fiarse de la cabeza.

Fíjese, mientras tanto, en lo que el programa de la figura no puede hacer: plantar once flores y saltarse la séptima, o parar antes si se acaba el agua. Una máquina que solo repite hace siempre lo mismo el mismo número de veces. Para que cada vuelta pueda elegir qué hacer hace falta una instrucción nueva, y es la del siguiente capítulo: la condición.

Capítulo 5 de 8

La condición

Informática 727 palabras artículo suelto ↗

La condición es la instrucción que convierte una ruta fija en una decisión: «si esto se cumple, haz aquello; si no, haz lo otro». Hasta ella, un programa era una lista que se ejecutaba entera, siempre igual, de la primera línea a la última. Con ella, la ejecución llega a un punto, hace una pregunta, y según la respuesta toma un camino u otro. La pregunta tiene una restricción que lo cambia todo: solo admite dos respuestas, sí o no. Es el bit del primer capítulo vuelto a aparecer: en el fondo de cada decisión que toma un programa hay una pregunta cuya respuesta cabe en un solo bit.

La figura de esta página dibuja una norma completa como árbol de decisiones. La norma de la piscina dice que los menores de doce años solo entran acompañados de un adulto, y eso son dos preguntas encadenadas: «¿edad ≥ 12?» y, solo si la primera sale que no, «¿acompañado?». Escrita como programa:

si edad ≥ 12
    entra solo
si no
    si acompañado
        entra con un adulto
    si no
        no entra
    fin si
fin si

Los tres resultados posibles (entra solo, entra con un adulto, no entra) cuelgan de las ramas. Mueve el deslizador de edad y marca o desmarca la casilla de acompañamiento: el camino por el árbol se ilumina en el momento, cada pregunta muestra su respuesta actual y el resultado final se enciende. Debajo de cada resultado, un contador recuerda cuántas de las veintiséis combinaciones de entrada terminan en él.

Interactivo Cruza el deslizador por el doce y verás el camino saltar de rama; marca y desmarca «acompañado» con un menor de doce para ver la segunda pregunta decidir. Las combinaciones de cada resultado están contadas.

Fíjate en lo que el árbol hace visible y la frase escrita oculta: la segunda pregunta solo se hace cuando la primera sale que no. Un niño de catorce años no es evaluado sobre acompañamiento; su caso se resolvió en el primer rombo. Las condiciones anidadas no son dos preguntas sino una pregunta y, en una de sus ramas, otra pregunta. Esa asimetría es exactamente lo que hace difícil leer código con muchas condiciones dentro de condiciones: cada «si» duplica el número de caminos posibles, y a los cuatro o cinco niveles ya hay más caminos de los que una cabeza puede recorrer. De ahí el oficio de quien programa: dibujar el árbol, aunque sea mentalmente, antes de escribir las líneas.

Que la pregunta sea de sí o no no es un capricho de los lenguajes, es su fundamento físico. George Boole mostró en 1854, en An Investigation of the Laws of Thought, que toda la lógica de las clases podía escribirse como un álgebra de dos valores, verdadero y falso, con tres operaciones. Cuando un siglo después las máquinas se construyeron con interruptores de dos estados, el álgebra de Boole era la descripción matemática exacta de lo que los circuitos hacían, y la condición de un programa es hoy literalmente eso: un bit calculado que decide por dónde sigue la ejecución. Ada Lovelace ya había imaginado la posibilidad en sus notas a la máquina de Babbage, en 1843: una máquina que pudiera variar su conducta según el resultado de sus propios cálculos dejaría de ser una calculadora para convertirse en otra cosa.

Hay una pieza más en el dibujo que merece atención: el «si no». No dice «si no, haz tal cosa concreta»; dice «si no, todo lo demás». El else es la rama de los casos que nadie enumeró, y por eso es donde viven los errores y los agravios: la norma que premia a quien tiene veinticinco años y carnet, y en el «si no» mete al de sesenta y cinco que nunca tuvo carnet, al menor acompañado y al que rellena mal el formulario. En 1966, Corrado Böhm y Giuseppe Jacopini demostraron que secuencia, condición y bucle bastan para escribir cualquier programa, y Edsger Dijkstra llevó la consecuencia a la práctica dos años después con su carta contra el salto incondicional: si el programa son estos tres ladrillos, se puede razonar sobre él pieza a pieza. La condición es el más delicado de los tres, porque es el único que decide. Un programa que solo repite hará siempre lo mismo con todos; uno que decide puede tratar distinto a cada caso, y conviene que lo haga por las razones escritas en las preguntas, no por accidente en la rama del «si no».

Un programa lleno de decisiones acaba repitiendo sus reglas: la misma comprobación escrita en doce sitios, cada copia esperando el día en que alguien corrija once. De eso trata el siguiente capítulo: la función, o cómo darle nombre a una regla para escribirla una sola vez.

Capítulo 6 de 8

La función

Informática 749 palabras artículo suelto ↗

La función es una regla con nombre: entra un valor por arriba, la regla lo transforma dentro, y sale un resultado por abajo. con IVA es una función: entra un precio, sale ese precio multiplicado por 1,21. Hasta aquí, nada que no hiciera una línea suelta de las del capítulo de las variables. Lo que la función añade es el nombre, y el nombre es la pieza importante, porque permite usar la regla sin tenerla presente. Quien escribe total = con IVA(precio) ya no piensa en el 1,21: piensa en lo que quiere decir. Ese gesto (meter una regla en una caja, ponerle una etiqueta y trabajar desde entonces con la etiqueta) es la abstracción, y es la herramienta principal con la que se construye todo el software que existe, desde una hoja de cálculo hasta un sistema operativo.

La figura de esta página dibuja dos funciones como máquinas encadenadas. La primera, con IVA, multiplica por 1,21; la segunda, a céntimos, redondea a dos decimales, y la salida de la primera entra en la segunda:

función con IVA(precio)
    devolver precio × 1,21

función a céntimos(importe)
    devolver importe redondeado a dos decimales

total = a céntimos(con IVA(12,50))

Es exactamente lo que hace cualquier programa real: encadenar máquinas con nombre. Mueve el deslizador del precio y verás el valor viajando por la cadena con sus tres escrituras (12,50 €, 15,125 €, 15,13 €), la intermedia con tres decimales para que se vea qué trabajo hace la segunda máquina. Pulsa sobre una máquina para cerrarle la tapa: sigue transformando igual, pero ya solo muestra su nombre. Y debajo hay una segunda entrada, con su propio deslizador, que pasa por una tercera instancia de con IVA: la misma máquina trabajando otra vez con otro precio, porque una función escrita una vez se puede usar las veces que haga falta.

Interactivo Mueve los precios, encadena las dos máquinas y ciérrales la tapa con un clic: la cadena sigue funcionando idéntica aunque solo se vean los nombres. Fíjate en el resultado intermedio con tres decimales.

La idea tiene una historia doble, matemática e industrial. En matemáticas, la palabra la acuñó Leibniz a finales del siglo XVII para las cantidades que dependen de una curva, y la noción moderna (cada entrada tiene exactamente una salida) la fijó Euler en su Introductio de 1748. En computación, la pieza práctica fue la subrutina: David Wheeler la describió en 1952 como lo que permitía al EDSAC de Cambridge tener una biblioteca (reglas escritas una vez por alguien que las había pensado bien, y usadas después por todos sin releerlas). Cuatro años después, el manual de FORTRAN ya incluía la FUNCTION como instrucción del lenguaje. La biblioteca de subrutinas es, en miniatura, toda la economía del software: alguien escribe a céntimos con cuidado una vez, y diez mil programas redondean bien sin que sus autores hayan tenido que saber cómo se redondea.

La tapa que se cierra en la figura es el punto sutil. Cuando la tapa está abierta ves la regla: multiplicar por 1,21. Cuando está cerrada ves solo el nombre, y el programa funciona idéntico. Esa posibilidad de olvidar el interior es lo que permite construir programas grandes: nadie puede tener en la cabeza los millones de líneas de un navegador, pero cualquiera puede manejar una cadena de veinte nombres. El precio de esa comodidad es que la caja puede estar mal por dentro y el nombre no avisar: con IVA podría estar multiplicando por 1,19 desde una actualización, y el código que la usa seguiría leyéndose igual de razonable. Las funciones se comprueban por separado, con entradas conocidas y resultados esperados, porque la confianza que da el nombre hay que merecerla.

Hay dos reglas de higiene que distinguen una buena caja de una trampa. La primera es que la función solo mire lo que entra por su entrada y solo cambie lo que sale por su salida: una con IVA que además anotara el precio en una caja global funcionaría, pero cada uso dejaría una huella invisible en el estado, y dos llamadas con la misma entrada podrían dar resultados distintos según lo que hubiera pasado antes. La segunda es que el nombre diga la verdad entera: con IVA que además aplicara un descuento tendría una tapa mentirosa, y el código que la usa decidiría mal sin saberlo. Entrada clara, salida clara, nombre honesto: con esas tres condiciones, una caja se puede cerrar sin miedo.

Y hay veces en que ni con la caja comprobada el programa hace lo que su autor quería. La regla es correcta, la máquina la ejecuta sin un fallo, y el resultado está mal: el error estaba en lo que el autor pidió. De eso trata el siguiente capítulo: la depuración, o qué hacer cuando el programa funciona y no sirve.

Capítulo 7 de 8

La depuración

Informática 689 palabras artículo suelto ↗

La depuración es el oficio de cerrar la distancia entre lo que el programa dice y lo que su autor quiso decir. La máquina nunca está confundida: ejecuta exactamente lo escrito, línea a línea, sin cansancio y sin imaginación. Cuando el resultado está mal, el error no está en la ejecución sino en la petición, y encontrarlo exige leer el propio programa con los ojos de la máquina, no con los de la intención. Es una disciplina desagradable porque la dificultad no es técnica sino de honestidad: hay que soltar lo que se quiso decir y mirar lo que está escrito.

La figura de esta página monta el error de principiante por excelencia. Una calle de L metros necesita una farola por metro, y el programa las coloca con un bucle: «para i desde 0 hasta L − 1: farola en el metro i». Ejecútalo paso a paso con «Un paso» y verás encenderse cada farola en su posición, ocho farolas para ocho metros. Todo parece bien hasta que miras el extremo derecho: del metro 7,5 al 8, la calle queda a oscuras. El bucle contó los tramos y la calle necesita los puntos: ocho tramos son nueve puntos, la farola del principio y una más por cada tramo. Es el error de la valla (quien planta una valla de cien metros con un poste por metro y pide cien postes se queda corto uno) y tiene su propio nombre en el oficio: off-by-one, desviado en uno. El desplegable ofrece las tres escrituras: la que falla al final, la que falla al principio, y la correcta, que termina con un «✓ calle completa». Cambia la longitud de la calle con el deslizador y el fallo se reproduce a cualquier escala.

Interactivo Ejecuta la versión «hasta L − 1» y mira el último medio metro quedarse oscuro; cambia a «desde 1 hasta L» para ver el mismo despiste por el otro extremo, y a «hasta L» para la calle completa.

La palabra tiene su leyenda fundacional, y la leyenda es un objeto físico. El 9 de septiembre de 1947, el equipo del Mark II de Harvard encontró la máquina fallando y la causa fue una polilla atrapada en un relé; pegaron el insecto en el cuaderno de bitácora con la anotación «primer caso real de bug encontrado», y ese cuaderno se conserva en el Smithsonian. Pero la palabra era ya vieja: Edison la usaba en 1878 en sus cartas para los fallos pequeños y escurridizos de sus inventos. Lo que Harvard aportó no fue el nombre sino el ejemplar disecado. Y lo que la anécdota esconde es que la polilla es el caso amable: la mayoría de los errores no se pueden pegar en un cuaderno porque están hechos de la misma materia que el programa. Maurice Wilkes, que construyó el EDSAC, lo escribió en sus memorias con la claridad del que lo vivió en 1949: de camino a la sala de la máquina comprendió de golpe que buena parte del resto de su vida se iría en encontrar errores en sus propios programas.

La manera profesional de encontrarlos no es la que estrena todo principiante (cambiar algo al azar, ejecutar, rezar) sino la que ya conoces del capítulo de las variables: trazar el programa a mano con el caso más pequeño que falle. Una calle de un metro: el bucle coloca una farola, en el metro 0, y el plano pedía dos, la del 0 y la del 1. Con L = 1 el error cabe entero en la cabeza, y la traza de dos líneas lo señala sin piedad. Kernighan y Plauger lo dijeron con otra vuelta de tuerca: depurar es el doble de difícil que escribir el programa, así que quien escribe al límite de su ingenio no está, por definición, cualificado para depurarlo. La moraleja práctica: los bucles se comprueban en los extremos, con el caso vacío, el de un elemento y el límite exacto, porque es donde viven los off-by-one; y el error que sobrevive años en un programa es siempre el que nadie contó.

Trazar a mano es ejecutar el programa en la cabeza, instrucción a instrucción. Pero ¿qué es exactamente una instrucción para la máquina, que no lee nuestro pseudocódigo? El siguiente capítulo abre ese último escalón: qué hay entre el texto que se escribe y el proceso que corre.

Capítulo 8 de 8

Del código al proceso

Informática 690 palabras artículo suelto ↗

El código fuente que escribe un programador no es lo que la máquina ejecuta: es un texto, escrito para ser leído por personas, y entre ese texto y la ejecución hay una traducción. La máquina solo entiende instrucciones elementales (mueve este valor a esta caja, suma estos dos, salta a esta línea si el resultado fue cero) y el programa que convierte el texto en esa lista de instrucciones se llama compilador o intérprete, según traduzca de una vez o sobre la marcha. Ese escalón es el que falta para cerrar el itinerario: los bits, las instrucciones, las cajas, los bucles y las condiciones existen de verdad, en el nivel de abajo, y el texto es solo su forma legible.

La figura de esta página muestra la traducción en la máquina más pequeña honesta: una máquina de pila. A la izquierda está el fuente; en medio, la lista de instrucciones a la que se traduce; a la derecha, la pila y la memoria:

resultado = 3 + 4 × 5

PUSH 3
PUSH 4
PUSH 5
MUL
ADD
GUARDAR resultado

PUSH pone un valor en la cima de la pila; MUL y ADD sacan los dos de arriba y dejan el resultado; GUARDAR vacía la pila en la caja con nombre. Ejecuta con «Un paso» y observa: la máquina no ve la expresión entera ni sabe qué es la precedencia, solo ejecuta la instrucción que le toca. Y ahora cambia el fuente a (3 + 4) × 5: la lista de instrucciones es otra, con ADD y MUL intercambiados, y el resultado es 35 en lugar de 23:

resultado = (3 + 4) × 5

PUSH 3
PUSH 4
ADD
PUSH 5
MUL
GUARDAR resultado

La precedencia de operadores, que en la escuela se memoriza como una norma, resulta ser una decisión del traductor sobre el orden de las instrucciones, y el paréntesis es la manera de cambiar esa decisión.

Interactivo Ejecuta las dos versiones paso a paso: mismos números, distinto orden de instrucciones, 23 contra 35. La pila crece con los PUSH y se vacía con cada operación.

Que el texto y la ejecución sean cosas distintas es una de las ideas más grandes del siglo XX, y tiene fecha y lugar: 1945, Princeton y Filadelfia. La idea del programa almacenado (que las instrucciones de la máquina vivan en la misma memoria que sus datos, escritas como números) aparece en el informe del EDVAC de von Neumann y en el Proposed Electronic Calculator de Turing, y la desarrollaron Burks, Goldstine y el propio von Neumann en el informe del IAS de 1946. La consecuencia es la que ves en la figura: si las instrucciones son datos, un programa puede escribir las instrucciones de otro programa. El compilador no es una herramienta más: es el programa que existe porque las instrucciones son números en memoria. Grace Hopper lo comprobó en 1952 con el A-0, el primer traductor, y contó después que nadie lo creía: tocaba explicar una y otra vez que la máquina podía escribir su propio código máquina.

El modelo de pila de la figura tampoco es un juguete didáctico. Edsger Dijkstra usó exactamente esa idea (una pila para los valores pendientes) para construir el traductor de ALGOL 60 del X1 en Ámsterdam, y las máquinas virtuales que ejecutan hoy Java o Python funcionan sobre el mismo principio. Cuando en la pila quedan tres valores y llega MUL, la máquina no pregunta de dónde salieron: toma los dos de arriba. Esa indiferencia es lo que hace al modelo universal (cualquier expresión, por larga que sea, se ejecuta con las mismas cuatro instrucciones) y lo que hace que los errores de pila sean los más oscuros de depurar: el valor equivocado no lleva etiqueta.

Con esto se cierra el recorrido. Un programa es un texto; el texto se traduce a instrucciones; las instrucciones mueven valores entre cajas, deciden por dónde seguir, repiten lo que cambia y se agrupan en máquinas con nombre. Todo lo que hace un ordenador (cada aplicación que has usado hoy) está hecho de esas piezas y de ninguna más. La pregunta siguiente ya no es cómo ejecuta la máquina sino cómo se organizan los datos para que la ejecución sea rápida: qué estructuras caben en esas cajas y qué cuesta buscar en cada una. De eso trata el itinerario «Estructuras de datos que se ven», que empieza midiendo en el navegador lo que este acaba de explicar.

Este itinerario ordena artículos de la enciclopedia; cada uno vive también suelto, con sus fuentes y su historial. ¿Le falta un capítulo o le sobra uno? Dínoslo.

Todos los itinerarios →