En programación, un anamorfismo es una función que genera una secuencia mediante la aplicación repetida de la misma a su resultado anterior. Se parte de un valor A y se le aplica una función f para obtener B. Luego se aplica f a B para obtener C, y así sucesivamente hasta que se cumple una condición de terminación. El anamorfismo es la función que genera la lista de A, B, C, etc. Se puede pensar en el anamorfismo como el despliegue del valor inicial en una secuencia.
La descripción anterior para el laico se puede expresar de manera más formal en la teoría de categorías : el anamorfismo de un tipo coinductivo denota la asignación de una coálgebra a su único morfismo a la coálgebra final de un endofunctor . Estos objetos se utilizan en la programación funcional como despliegues .
El dual categórico (la función que más naturalmente se considera el "opuesto") del anamorfismo es el catamorfismo .
Anamorfismos en la programación funcional
En programación funcional , un anamorfismo es una generalización del concepto de despliegue en listas coinductivas . Formalmente, los anamorfismos son funciones genéricas que pueden construir de forma corecursiva un resultado de un tipo determinado y que están parametrizadas por funciones que determinan el siguiente paso de la construcción.
El tipo de datos en cuestión se define como el punto fijo más grande ν X . FX de un functor F . Por la propiedad universal de las coálgebras finales, hay un morfismo de coálgebra único A → ν X . FX para cualquier otra F -coálgebra a : A → FA . Por lo tanto, se pueden definir funciones de un tipo A _en_ un tipo de datos coinductivo especificando una estructura de coálgebra a en A .
Ejemplo: Listas potencialmente infinitas
Como ejemplo, el tipo de listas potencialmente infinitas (con elementos de un tipo fijo `value` ) se define como el punto fijo ` [value] = ν X . value × X + 1` , es decir, una lista consta de un valor y otra lista, o está vacía. Una definición (pseudo) Haskell podría ser similar a esta:
datos [ valor ] = ( valor : [ valor ]) | []Es el punto fijo del functor F value, donde:
datos Quizás a = Solo a | Nada datos F valor x = Quizás ( valor , x )Se puede comprobar fácilmente que, efectivamente, el tipo [value]es isomorfo a F value [value], y por lo tanto [value]es el punto fijo. (Cabe destacar también que en Haskell, los puntos fijos mínimo y máximo de los functores coinciden; por consiguiente, las listas inductivas son lo mismo que las listas coinductivas, potencialmente infinitas).
El anamorfismo para listas (conocido entonces como `unfold` ) construye una lista (potencialmente infinita) a partir de un valor de estado. Normalmente, `unfold` toma un valor de estado xy una función fque produce un par formado por un valor y un nuevo estado, o un singleton para marcar el final de la lista. El anamorfismo comienza con una semilla inicial, calcula si la lista continúa o termina y, en caso de que la lista no esté vacía, antepone el valor calculado a la llamada recursiva al anamorfismo.
Una definición en Haskell de un despliegue, o anamorfismo para listas, llamado ana, es la siguiente:
ana :: ( estado -> Quizás ( valor , estado )) -> estado -> [ valor ] ana f estadoAntiguo = caso f estadoAntiguo de Nada -> [] Solo ( valor , estadoNuevo ) -> valor : ana f estadoNuevoAhora podemos implementar funciones bastante generales usando ana , por ejemplo una cuenta regresiva:
f :: Int -> Maybe ( Int , Int ) f actual = let oneSmaller = actual - 1 en si oneSmaller < 0 entonces Nada más Just ( oneSmaller , oneSmaller )Esta función decrementará un número entero y lo mostrará al mismo tiempo, hasta que sea negativo, momento en el que marcará el final de la lista. Correspondientemente, ana f 3calculará la lista [2,1,0].
Anamorfismos en otras estructuras de datos
Se puede definir un anamorfismo para cualquier tipo recursivo, según un patrón genérico, generalizando la segunda versión de ana para listas.
Por ejemplo, el despliegue para la estructura de datos de árbol.
Datos Árbol a = Hoja a | Rama ( Árbol a ) a ( Árbol a )es el siguiente
ana :: ( b -> Either a ( b , a , b )) -> b -> Tree a ana unspool x = case unspool x of Left a -> Leaf a Right ( l , x , r ) -> Branch ( ana unspool l ) x ( ana unspool r )Para ver mejor la relación entre el tipo recursivo y su anamorfismo, tenga en cuenta que Treey Listse pueden definir de la siguiente manera:
newtype List a = List { unCons :: Maybe ( a , List a )}nuevo tipo Árbol a = Árbol { unNodo :: O bien a ( Árbol a , a , Árbol a ))}La analogía con anaaparece al renombrar ben su tipo:
newtype List a = List { unCons :: Maybe ( a , List a )} anaList :: ( list_a -> Maybe ( a , list_a )) -> ( list_a -> List a )newtype Tree a = Tree { unNode :: Either a ( Tree a , a , Tree a ))} anaTree :: ( tree_a -> Either a ( tree_a , a , tree_a )) -> ( tree_a -> Tree a )Con estas definiciones, el argumento del constructor del tipo tiene el mismo tipo que el tipo de retorno del primer argumento de ana, con las menciones recursivas del tipo reemplazadas por b.
Historia
Una de las primeras publicaciones que introdujo la noción de anamorfismo en el contexto de la programación fue el artículo Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire , [ 1 ] de Erik Meijer et al. , que se encontraba en el contexto del lenguaje de programación Squiggol .
Aplicaciones
Funciones como zipy iterateson ejemplos de anamorfismos. ziptoma un par de listas, digamos ['a','b','c'] y [1,2,3] y devuelve una lista de pares [('a',1),('b',2),('c',3)]. Iteratetoma un objeto, x, y una función, f, de tales objetos a tales objetos, y devuelve la lista infinita que resulta de la aplicación repetida de f, es decir, la lista [x, (fx), (f (fx)), (f (f (fx))), ...].
zip ( a : as ) ( b : bs ) = si ( as == [] ) || ( bs == [] ) -- || significa 'o' entonces [( a , b )] sino ( a , b ) : ( zip como bs ) iterar f x = x : ( iterar f ( f x ))Para demostrar esto, podemos implementar ambos usando nuestro despliegue genérico, ana, usando una rutina recursiva simple:
zip2 = ana unsp fin donde fin ( as , bs ) = ( as == [] ) || ( bs == [] ) unsp (( a : as ), ( b : bs )) = (( a , b ),( as , bs ))iterate2 f = ana ( \ a -> ( a , f a )) ( \ x -> False )En un lenguaje como Haskell, incluso las funciones abstractas fold, unfoldy anason simplemente términos definidos, como hemos visto en las definiciones dadas anteriormente.
Anamorfismos en la teoría de categorías
En la teoría de categorías , los anamorfismos son el dual categórico de los catamorfismos (y los catamorfismos son el dual categórico de los anamorfismos).
Eso significa lo siguiente. Supongamos que ( A , fin ) es una F -coalgebra final para algún endofunctor F de alguna categoría en sí misma. Por lo tanto, fin es un morfismo de A a FA , y como se supone que es final sabemos que siempre que ( X , f ) sea otra F -coalgebra (un morfismo f de X a FX ), habrá un único homomorfismo h de ( X , f ) a ( A , fin ), es decir, un morfismo h de X a A tal que fin . h = Fh . f . Entonces, para cada f denotamos por ana f ese morfismo h especificado de forma única .
En otras palabras, tenemos la siguiente relación definitoria, dados algunos valores fijos de F , A y fin como se indicó anteriormente:
Notación
Una notación para una f encontrada en la literatura esLos soportes utilizados se conocen como soportes de lente , y a partir de ellos, a veces se hace referencia a los anamorfismos como lentes .
Véase también
- Morfismo
- Morfismos de F-álgebras
- De un álgebra inicial a un álgebra: Catamorfismo
- Anamorfismo seguido de catamorfismo: Hilemorfismo
- Extensión de la idea de catamorfismos: Paramorfismo
- Extensión de la idea de anamorfismos: Apomorfismo
Referencias
- ↑ Meijer, Erik ; Fokkinga, Martín; Paterson, Ross (1991). "Programación funcional con bananas, lentes, sobres y alambre de púas": 124– 144. CiteSeerX 10.1.1.41.125 .
{{cite journal}}: Para citar una revista se requiere|journal=( ayuda )
Enlaces externos
- Anamorfismos en Haskell
- Teoría de categorías
- Esquemas de recursión