Lic. Carlos Enrique Loria Beeche.
El arte silencioso detrás del software moderno
Hay temas que uno no olvida, no porque fueran fáciles, sino porque marcaron una forma de pensar.
Cuando estudiaba Ciencias de la Computación, tuve como profesor a Adolfo Di Mare Hering, quien me enseñó estructuras de datos dentro de un curso de Lenguaje de Máquinas; es decir, en un curso de programación en lenguaje ensamblador, trabajando sobre la arquitectura de una IBM System/360 Model 40, aquella máquina que en la historia de la Universidad de Costa Rica aparece recordada como Cleotilde.
Años más tarde, el profesor Di Mare sería también mi director de tesis de Licenciatura. Por eso este recuerdo no es solo una anécdota de aula: es también una forma de gratitud hacia quien acompañó una parte importante de mi formación como estudiante, como programador y como profesional.
Durante mucho tiempo, en mi memoria afectiva, todo ese mundo universitario de computación antigua quedaba asociado con el nombre de Matilde, porque Matilde fue la computadora legendaria de la UCR, la primera gran referencia de aquellos años iniciales. Pero al revisar mejor la historia, conviene hacer la precisión: Matilde fue una IBM 1620; la IBM 360/40 fue Cleotilde. Y esa diferencia, lejos de quitarle encanto al recuerdo, lo ubica mejor en su verdadero lugar.
A primera vista, alguien podría preguntarse: ¿qué tienen que ver las estructuras de datos con el lenguaje ensamblador?
Con los años, la respuesta se vuelve más clara.
En un lenguaje tan cercano a la máquina, donde cada instrucción importa, donde los registros tienen nombre y propósito, donde la memoria no es una abstracción lejana sino un territorio concreto que se recorre dirección por dirección, uno aprende que programar no consiste solamente en escribir órdenes. Programar es organizar el pensamiento para que la máquina pueda ejecutarlo.
Y cuando pienso en aquel curso, hay una instrucción que nunca olvidé:
Branch and Link Register.
En particular, recuerdo aquella forma clásica de llamar una subrutina usando el registro 14 como lugar donde se guardaba la dirección de retorno. En términos sencillos: el programa saltaba a otra parte para ejecutar una rutina, pero antes debía dejar guardado el camino para poder regresar.
Ese detalle técnico me quedó grabado.
L 15,=V(SUBRUTINA) * Carga en el registro 15 la dirección de entrada
* de la subrutina que se desea llamar.
BALR 14,15 * Branch and Link Register.
* Guarda en el registro 14 la dirección de retorno,
* es decir, la dirección de la siguiente instrucción.
* Luego salta a la dirección contenida en el registro 15.
... * Al regresar de la subrutina, el programa continúa aquí.
SUBRUTINA DS 0H * Inicio simbólico de la subrutina.
... * Instrucciones propias de la subrutina.
BR 14 * Branch Register.
* Regresa a la dirección guardada en el registro 14.
En términos sencillos, el programa cargaba en el registro 15 la dirección de la subrutina que quería ejecutar. Luego, con BALR 14,15, saltaba hacia esa subrutina, pero antes dejaba guardada en el registro 14 la dirección a la que debía regresar.
El regreso se hacía con una instrucción como BR 14. Allí estaba la elegancia del mecanismo: avanzar, ejecutar y volver. No perder el camino. No perder el contexto.
En la IBM System/360, el registro 14 guardaba el camino de regreso. Con los años comprendí que programar bien consiste precisamente en eso: saber avanzar sin perder la estructura que permite volver.

Porque allí, en una instrucción de bajo nivel, había una enseñanza profunda: avanzar no basta; hay que saber volver. Ejecutar no basta; hay que conservar el contexto. Resolver una parte del problema no basta; hay que mantener el orden de todo el programa.
Años después, al pensar en las estructuras de datos, entiendo mejor por qué nuestro profesor quería que aprendiéramos estas cosas en un curso de lenguaje ensamblador. No se trataba solamente de aprender arrays, listas, pilas o colas como conceptos aislados. Se trataba de aprender a pensar con disciplina, con estrategia, con respeto por la memoria.
Porque la memoria, cuando se organiza bien, deja de ser simplemente espacio disponible.
Se convierte en inteligencia.
Gracias, Adolfo.
El libro era Assembler Language for FORTRAN, COBOL, and PL/I Programmers, de Shan S. Kuo. El título mismo dice mucho de aquella época: no se trataba de aprender ensamblador como una curiosidad aislada, sino como un puente para entender lo que ocurría debajo de los lenguajes de alto nivel. Para quienes programaban en FORTRAN, COBOL o PL/I, el lenguaje ensamblador abría una ventana hacia la arquitectura real de la máquina: registros, direcciones, saltos, subrutinas, áreas de memoria y convenciones de llamada. Era una forma exigente, pero muy formativa, de descubrir que el software no flota en el aire; siempre descansa sobre una organización concreta de la memoria y del procesador.

Hay objetos que guardan más que papel. Este libro, recomendado por el profesor Di Mare y comprado con esfuerzo por mis padres, conserva todavía una dedicatoria manuscrita de mi mamá. Al reencontrarlo tantos años después, comprendí que este post no debía ser solamente una explicación sobre estructuras de datos. También debía ser un acto de memoria y gratitud: hacia mis profesores, hacia mis padres, y hacia esa etapa en que aprender computación exigía paciencia, disciplina y mucha ilusión.
Antes de que la memoria de una computadora se convirtiera en inteligencia, hubo otra memoria más profunda: la memoria agradecida de quienes hicieron posible que uno estudiara.
¿Por qué hablar hoy de estructuras de datos?
Hoy vivimos rodeados de software. Usamos teléfonos, redes sociales, aplicaciones bancarias, sistemas de navegación, motores de búsqueda, plataformas de comercio electrónico, sistemas de facturación, bases de datos, inteligencia artificial y servicios que parecen responder de forma casi instantánea.
Pero debajo de esa aparente magia hay decisiones muy concretas. Decisiones sobre cómo guardar, buscar, ordenar, recorrer, modificar y relacionar datos.
Una estructura de datos es, dicho de manera sencilla, una forma organizada de guardar información. Pero esa definición se queda corta. En realidad, una estructura de datos es una decisión de diseño. Es una manera de decirle a la computadora: “esto lo vamos a ordenar así porque luego necesitaremos usarlo de cierta forma”.
Un programador principiante puede pensar que lo importante es simplemente “hacer que funcione”. Pero con el tiempo uno descubre otra dimensión: no basta con que funcione; importa también cómo funciona, cuánto tarda, cuánta memoria consume, qué tan fácil será mantenerlo y qué tan bien responderá cuando crezca la cantidad de información.
Por eso las estructuras de datos siguen siendo fundamentales. No pertenecen únicamente a un lenguaje de programación. Se pueden estudiar en C, C++, Java, C#, Python, JavaScript o cualquier otro lenguaje moderno. Cambia la sintaxis, cambian las bibliotecas, cambian las comodidades del programador; pero la idea profunda permanece.
Las estructuras de datos son parte del arte silencioso detrás del software moderno.
En aquellos años también aparecía, como referencia casi inevitable, el nombre de Donald E. Knuth. Su obra The Art of Computer Programming tenía —y sigue teniendo— ese carácter de libro mayor: exigente, profundo, a veces intimidante, pero lleno de una idea fundamental. La programación no es solamente una técnica para producir resultados; es también un arte de precisión. Knuth nos enseñaba que detrás de un algoritmo hay belleza, orden, análisis y responsabilidad intelectual. Por eso, hablar de estructuras de datos es también volver a esa tradición en la que programar significaba pensar cuidadosamente antes de escribir una sola línea de código.
Las ocho estructuras que vamos a recorrer
En esta primera entrega no pretendo agotar el tema. Más bien quiero abrir la puerta. Pero sí conviene presentar el mapa general de la saga, porque estas estructuras no son adornos académicos: son herramientas fundamentales para pensar correctamente el software.
Vamos a recorrer ocho estructuras de datos fundamentales:
- Array, o arreglo.
- Linked List, o lista enlazada.
- Stack, o pila.
- Queue, o cola.
- Hash Table, o tabla hash.
- Binary Search Tree, o árbol binario de búsqueda.
- Heap, una estructura basada en prioridades.
- Graph, o grafo.
El orden no es casual. Empezamos con estructuras más sencillas y visibles, y poco a poco avanzamos hacia estructuras más poderosas, capaces de representar relaciones complejas, prioridades, jerarquías y caminos.
1. Array: el estacionamiento numerado de la memoria
Un array, o arreglo, es una de las estructuras más básicas y más importantes. Se puede imaginar como un estacionamiento de un centro comercial: cada espacio tiene un número, y si conocemos ese número podemos ir directamente al lugar que necesitamos.

En términos técnicos, un array guarda elementos en posiciones consecutivas de memoria. Cada elemento se accede mediante un índice. Por eso, si tenemos un arreglo de nombres, números, productos o registros, podemos pedir directamente el elemento que está en la posición 0, 1, 2, 3, y así sucesivamente.
La gran ventaja del array es el acceso directo. Si sé que necesito el elemento número 50, no tengo que revisar los 49 anteriores. Voy directamente a esa posición.
Pero esa eficiencia tiene un costo. Si quiero insertar un elemento en medio del arreglo, muchas veces debo mover otros elementos para abrir espacio. Si quiero eliminar uno, también puede ser necesario reacomodar los demás. Por eso el array es excelente cuando queremos acceso rápido por posición, pero no siempre es la mejor opción cuando necesitamos insertar y borrar constantemente en medio de la colección.
En la vida real, los arrays aparecen por todas partes: listas de precios, colecciones de valores, resultados ordenados, vectores, matrices, tablas, buffers y muchas estructuras internas de los lenguajes de programación.
El array nos enseña una primera lección: cuando la información está bien numerada y ordenada, llegar a ella puede ser casi instantáneo.
2. Linked List: la búsqueda del tesoro
Una linked list, o lista enlazada, funciona de una manera muy distinta. En lugar de guardar todo como una fila compacta y numerada, cada elemento guarda su propio valor y además una referencia al siguiente elemento.

Por eso se puede comparar con una búsqueda del tesoro. Uno encuentra una pista, y esa pista le dice dónde está la siguiente. Luego la siguiente lleva a otra, y así sucesivamente.
Cada elemento de una lista enlazada suele llamarse nodo. Un nodo contiene dos cosas: el dato que queremos guardar y una referencia hacia el próximo nodo. En algunas listas, llamadas doblemente enlazadas, cada nodo también puede guardar una referencia al nodo anterior.
La ventaja de una lista enlazada es que insertar o eliminar elementos puede ser más flexible que en un array, especialmente cuando ya sabemos dónde queremos hacer el cambio. No siempre hay que mover toda una colección de datos. Basta con ajustar referencias.
Pero también hay un costo: no podemos ir directamente al elemento número 50 como en un array. Para llegar allí, normalmente debemos empezar desde el primer nodo y avanzar uno por uno hasta encontrarlo. Es decir, la lista enlazada es buena para reorganizar, pero no tan buena para acceso directo por posición.
Esta diferencia es fundamental. En programación, muchas decisiones consisten precisamente en escoger qué operación será más frecuente: buscar por posición, insertar, eliminar, recorrer, ordenar o reorganizar.
La lista enlazada nos recuerda que no toda información necesita estar en una fila compacta; a veces basta con saber cuál es el siguiente paso.
3. Stack: la pila y el recuerdo del último paso
Una stack, o pila, trabaja con una regla muy sencilla: el último elemento en entrar es el primero en salir. En inglés se le llama LIFO: Last In, First Out.

La analogía clásica es una pila de platos. Si colocamos un plato encima de otro, el primero que podemos quitar es el último que pusimos. No sacamos el plato de abajo sin mover los de arriba.
En el software, una pila aparece en muchas situaciones. Un ejemplo cotidiano es el famoso Control Z. Cuando deshacemos acciones en un editor de texto, normalmente se deshace primero la última acción realizada. Luego la anterior. Luego la anterior. Es una pila de acciones.
También aparece en la ejecución de programas. Cuando una función llama a otra función, y esa otra llama a otra más, el programa necesita recordar por dónde debe regresar. Allí aparece la idea de una pila de llamadas. Se van apilando contextos, direcciones de retorno, variables locales y estados temporales.
Por eso, al recordar el registro 14 y la instrucción BALR 14,15 en la IBM System/360, la conexión con las estructuras de datos se vuelve más clara. El programa no solo saltaba a una subrutina; necesitaba conservar la información necesaria para volver. Esa necesidad de guardar contexto está muy cerca del corazón conceptual de una pila.
Las operaciones básicas de una pila suelen llamarse push y pop. Con push agregamos un elemento arriba de la pila. Con pop quitamos el elemento superior. También puede existir una operación para mirar el elemento superior sin quitarlo, a veces llamada peek.
La pila nos enseña que el orden de regreso importa: a veces, para avanzar correctamente, primero hay que resolver lo último que quedó pendiente.
4. Queue: la cola y la justicia del turno
Una queue, o cola, funciona con la regla contraria a la pila. Aquí, el primero en entrar es el primero en salir. En inglés se le llama FIFO: First In, First Out.

La analogía más sencilla es una fila de banco. Quien llega primero debe ser atendido primero. También podemos pensar en una cola de impresión: los documentos se imprimen en el orden en que fueron enviados, salvo que exista alguna prioridad especial.
Las colas son esenciales en sistemas donde el orden de llegada importa. Por ejemplo, procesamiento de tareas, mensajes pendientes, solicitudes a un servidor, trabajos de impresión, atención de eventos, comunicaciones entre procesos y sistemas que reciben muchas peticiones al mismo tiempo.
Las operaciones básicas suelen llamarse enqueue y dequeue. Con enqueue agregamos un elemento al final de la cola. Con dequeue sacamos el elemento del frente.
Una cola enseña una idea muy importante: no todo se resuelve por urgencia inmediata ni por último movimiento. A veces la regla correcta es respetar el orden de llegada. El software moderno usa colas constantemente para ordenar trabajo, evitar saturación y procesar tareas de forma controlada.
La cola nos recuerda que también en el software hay turnos, esperas y procesos que deben avanzar con orden.
Las estructuras que veremos después
Después de estas primeras cuatro estructuras, la saga avanzará hacia formas más poderosas de organizar información.
Una tabla hash permite encontrar información con gran rapidez, como si usáramos una fórmula para ir directamente a la sección correcta de un diccionario.
Un árbol binario de búsqueda permite organizar datos de manera jerárquica, descartando una parte importante de la información en cada decisión.
Un heap nos ayuda a trabajar con prioridades, como en una sala de urgencias donde no siempre pasa primero quien llegó primero, sino quien necesita atención más inmediata.
Y un grafo permite modelar relaciones complejas: ciudades conectadas por carreteras, personas conectadas en redes sociales, rutas posibles, dependencias, vínculos, mapas y caminos.
Estas estructuras merecen su propio espacio, porque en ellas el software empieza a parecerse mucho más al mundo real: no solo listas de datos, sino relaciones, prioridades, caminos y decisiones.
Una pequeña saga
Podríamos decir, con una sonrisa, que esta será una pequeña saga. Como en La guerra de las galaxias, habrá secuelas: no porque queramos complicar la historia, sino porque cada estructura merece su propio momento, su propio escenario y su propia batalla.
Algunas estructuras nos enseñan a ordenar. Otras nos enseñan a esperar. Otras nos enseñan a buscar rápido. Otras nos enseñan a dar prioridad. Y otras nos permiten descubrir caminos entre elementos que, a simple vista, parecían separados.
Mi intención no es escribir solamente para especialistas. Quisiera que estos textos también puedan leerlos familiares, amigos y personas curiosas que tal vez no programan todos los días, pero que sí viven rodeadas de software. Porque entender un poco cómo se organiza la información es también entender mejor el mundo digital en el que vivimos.
Esta será una pequeña saga sobre memoria, orden e inteligencia: una invitación a descubrir el arte silencioso detrás del software moderno.
De la memoria al pensamiento
Cuando vuelvo mentalmente a aquella IBM System/360 Model 40, a Cleotilde, al lenguaje ensamblador, al registro 14, a la instrucción BALR 14,15, al libro recomendado por mi profesor y a la dedicatoria de mi mamá, comprendo que las estructuras de datos no son solamente una materia universitaria.
Son una forma de educación del pensamiento.
Nos enseñan que no todo puede guardarse de cualquier manera. Nos enseñan que el orden importa. Nos enseñan que cada decisión tiene consecuencias. Nos enseñan que la eficiencia no aparece por accidente, sino por diseño.
Y quizá esa sea una lección que va más allá de la computación.
Porque también en la vida necesitamos estructuras: memoria para no olvidar, orden para no perdernos, prioridades para atender lo importante, caminos para avanzar y humildad para reconocer a quienes nos ayudaron a llegar hasta aquí.
Por eso inicio esta serie con gratitud.
Gratitud hacia mi profesor Adolfo Di Mare Hering, por haberme enseñado que detrás de cada instrucción puede esconderse una manera de pensar.
Gratitud hacia mis padres, por aquel libro comprado con esfuerzo.
Gratitud hacia mi mamá, por aquella dedicatoria que todavía conserva su letra, su cariño y su fe en mi camino.
Y gratitud también por la posibilidad de volver, tantos años después, sobre estos temas que siguen vivos.
Porque programar bien no es solamente dar órdenes a una máquina.
Es aprender a pensar con claridad, con estrategia y con respeto por la memoria.
Sugerencias para seguir la serie
En las próximas entregas recorreremos estas estructuras con ejemplos sencillos, analogías cotidianas y aplicaciones reales:
- Arrays y listas enlazadas: dos maneras de caminar por la memoria.
- Pilas y colas: el orden invisible de las acciones.
- Hash tables, árboles, heaps y grafos: cuando los datos aprenden a encontrar caminos.
