Translate

martes, 11 de junio de 2024

Más Funciones Recursivas en Erlang parte 4


Para llevar las cosas un poco más allá, escribiremos una función de compresión. Una función de compresión tomará dos listas de la misma longitud como parámetros y las unirá como una lista de tuplas que contienen dos términos. Nuestra propia función zip/2 se comportará de esta manera:


1> recursive:zip([a,b,c],[1,2,3]).

[{a,1},{b,2},{c,3}]


Dado que queremos que nuestros parámetros tengan la misma longitud, el caso base comprimirá dos listas vacías:


zip([],[]) -> [];

zip([X|Xs],[Y|Ys]) -> [{X,Y}|zip(Xs,Ys)].


Sin embargo, si desea una función zip más indulgente, puede decidir que finalice cada vez que finalice una de las dos listas. Por lo tanto, en este escenario, tiene dos casos base:


lenient_zip([],_) -> [];

lenient_zip(_,[]) -> [];

lenient_zip([X|Xs],[Y|Ys]) -> [{X,Y}|lenient_zip(Xs,Ys)].


Observe que no importa cuáles sean nuestros casos base, la parte recursiva de la función sigue siendo la misma. 

La recursividad de cola como se ve aquí no hace que la memoria crezca porque cuando la máquina virtual ve una función que se llama a sí misma en una posición de cola (la última expresión que se evaluará en una función), elimina el marco de pila actual. Esto se denomina optimización de llamada final (TCO) y es un caso especial de una optimización más general denominada optimización de última llamada (LCO).

LCO se realiza siempre que la última expresión que se evaluará en el cuerpo de una función es otra llamada de función. Cuando eso sucede, al igual que con el TCO, Erlang VM evita almacenar el marco de la pila. Como tal, la recursividad de cola también es posible entre múltiples funciones. Como ejemplo, la cadena de funciones a() -> b(). b()->c(). c()->a(). creará efectivamente un bucle infinito que no se quedará sin memoria ya que LCO evita el desbordamiento de la pila. Este principio, combinado con nuestro uso de acumuladores, es lo que hace que la recursividad de cola sea útil.