Las listas enlazadas en Elixir son una estructura fundamental que, aunque sencilla, tiene particularidades que vale la pena entender bien para aprovecharlas al máximo. A diferencia de las tuplas, que almacenan sus elementos de forma contigua en memoria, las listas enlazadas funcionan mediante una composición de cabeza y cola, lo que las hace especialmente útiles para ciertas operaciones y para el pattern matching.
Cuando hablamos de listas en Elixir, visualmente las vemos como una secuencia de elementos entre corchetes, por ejemplo [1, 2, 3, 4]. Sin embargo, internamente, esta lista no es simplemente un bloque de memoria con esos cuatro valores uno tras otro, sino que está formada por una cabeza y una cola. La cabeza es el primer elemento de la lista, en este caso 1, y la cola es el resto de la lista, es decir, [2, 3, 4].
Podemos obtener la cabeza de una lista usando la función hd, que nos devuelve el primer elemento, y la cola con la función tl, que nos devuelve el resto de la lista sin la cabeza. Por ejemplo, hd([1, 2, 3, 4]) nos da 1, y tl([1, 2, 3, 4]) nos da [2, 3, 4].
Esta estructura se repite recursivamente: la cola es a su vez otra lista con su propia cabeza y cola. Así, la cola [2, 3, 4] tiene cabeza 2 y cola [3, 4], y así sucesivamente hasta llegar a la lista vacía [], que es un caso especial que no tiene cabeza ni cola.
En Elixir, podemos construir listas explícitamente usando esta idea de cabeza y cola con la sintaxis [cabeza | cola]. Por ejemplo, si definimos:
base = []
uno = [1 | base]
dos = [2 | uno]
tres = [3 | dos]
cuatro = [4 | tres]
Aquí, uno es la lista [1], dos es [2, 1], tres es [3, 2, 1] y cuatro es [4, 3, 2, 1]. Esta forma de construir listas nos permite entender cómo se enlazan los elementos internamente.
El pattern matching con listas enlazadas es muy potente. Podemos extraer la cabeza y la cola de una lista directamente en una asignación, por ejemplo:
[cabeza | cola] = [1, 2, 3, 4, 5]
# cabeza = 1
# cola = [2, 3, 4, 5]
Además, podemos hacer pattern matching con varios elementos al principio y luego la cola, así:
[one, two | resto] = [1, 2, 3, 4, 5]
# one = 1
# two = 2
# resto = [3, 4, 5]
Es importante recordar que la barra vertical | siempre debe ir a la izquierda en la sintaxis de pattern matching para listas. No es válido poner la barra a la derecha de las comas, ya que la parte después de la barra representa la cola, que debe ser una lista.
También debemos tener cuidado con el número de elementos cuando hacemos pattern matching con comas. Si intentamos hacer coincidir un número fijo de elementos con variables y la lista no tiene exactamente ese número, obtendremos un error. Por eso, cuando no sabemos el tamaño exacto, es mejor usar la barra para capturar la cola restante.
Por ejemplo, esto es válido:
[head | tail] = [1, 2, 3]
# head = 1
# tail = [2, 3]
Pero esto puede fallar si la lista no tiene el número exacto de elementos:
[a, b, c] = [1, 2]
# Error: no hay suficientes elementos para hacer match
En resumen, las listas enlazadas en Elixir se basan en la idea de cabeza y cola, lo que facilita su manipulación mediante pattern matching y permite construir listas de forma recursiva y elegante. Esta estructura es diferente a las tuplas, que son bloques contiguos en memoria, y entender esta diferencia nos ayuda a escribir código más claro y eficiente en Elixir.