Una cremallera es una técnica para representar una estructura de datos agregada de manera que resulte conveniente escribir programas que la recorran arbitrariamente y actualicen su contenido, especialmente en lenguajes de programación puramente funcionales . La cremallera fue descrita por Gérard Huet en 1997. [ 1 ] Incluye y generaliza la técnica del búfer de huecos que a veces se utiliza con arreglos.
La técnica de la cremallera es general en el sentido de que puede adaptarse a listas , árboles y otras estructuras de datos definidas recursivamente . Estas estructuras de datos modificadas suelen denominarse "árbol con cremallera" o "lista con cremallera" para enfatizar que la estructura es conceptualmente un árbol o una lista, mientras que la cremallera es un detalle de la implementación.
Una explicación sencilla para un no experto sobre un árbol con cremallera sería un sistema de archivos informático común con operaciones para ir al nodo padre (a menudo cd ..) y para descender ( cd subdirectory). La cremallera es el puntero a la ruta actual. Internamente, las cremalleras son eficientes al realizar cambios (funcionales) en una estructura de datos, donde se devuelve una nueva estructura de datos ligeramente modificada tras una operación de edición (en lugar de modificar la estructura de datos actual).
Ejemplo: Recorrido bidireccional de una lista
Muchas estructuras de datos comunes en informática pueden expresarse como la estructura generada por unas pocas operaciones de constructor primitivas u operaciones de observador . Estas incluyen la estructura de listas finitas, que pueden generarse mediante dos operaciones:
Emptyconstruye una lista vacía,Cons(x, L)construye una lista anteponiendo o concatenando un valorxdelante de la listaL.
Una lista como esta [1, 2, 3]es, por lo tanto, la declaración Cons(1, Cons(2, Cons(3, Empty))). Es posible describir la ubicación en dicha lista como el número de pasos desde el principio de la lista hasta la ubicación de destino. Más formalmente, una ubicación en la lista es el número de Consoperaciones necesarias para reconstruir toda la lista a partir de esa ubicación particular. Por ejemplo, en Cons(1, Cons(2, Cons( X, Cons(4, Empty))))una Cons(2, L)y Cons(1, L)se requeriría una operación para reconstruir la lista con respecto a la posición X, también conocida como Cons( X, Cons(4, Empty)). Este registro junto con la ubicación se denomina representación comprimida de la lista o lista-comprimida.
Para ser claros, una ubicación en la lista no es solo el número de Consoperaciones, sino también toda la demás información sobre ellas Cons; en este caso, los valores que deben reconectarse. Aquí, estos valores pueden representarse convenientemente en una lista separada en el orden de aplicación desde la ubicación de destino. Específicamente, desde el contexto de "3" en la lista [1, 2, 3, 4], una grabación (comúnmente denominada "ruta") podría representarse como [2, 1]donde Cons(2, L)se aplica seguido de (Cons 1, L)para reconstituir la lista original comenzando desde [3, 4].
Un list-zipper siempre representa la estructura de datos completa. Sin embargo, esta información se presenta desde la perspectiva de una ubicación específica dentro de esa estructura de datos. En consecuencia, un list-zipper es un par que consta tanto de la ubicación como contexto o punto de partida, como de un registro o ruta que permite la reconstrucción desde esa ubicación de partida. En particular, el list-zipper en [1, 2, 3, 4]la ubicación "3" puede representarse como ([2, 1], [3, 4]). Ahora, si "3" se cambia a "10", entonces el list-zipper se convierte en ([2, 1], [10, 4]). La lista puede reconstruirse eficientemente: [1, 2, 10, 4]o recorrer otras ubicaciones hasta.
Con la lista representada de esta manera, es fácil definir operaciones relativamente eficientes en estructuras de datos inmutables como listas y árboles en ubicaciones arbitrarias. En particular, aplicar la transformación de cremallera a un árbol facilita la inserción o eliminación de valores en cualquier posición del mismo.
Contextos y diferenciación
El tipo de contextos de una cremallera se puede construir mediante una operación sobre el tipo original que está estrechamente relacionada con la derivada del cálculo a través de la descategorización . Los tipos recursivos a partir de los cuales se forman las cremalleras pueden verse como el punto fijo mínimo de un constructor de tipo unario de tipoPor ejemplo, con un constructor de tipo de orden superior.que construye el punto fijo más pequeño de su argumento, un árbol binario sin etiquetar puede representarse comoy una lista sin etiquetar puede tomar la formaAquí, la notación de exponenciación, multiplicación y suma corresponden a tipos de función , tipos de producto y tipos de suma respectivamente, mientras que los números naturales etiquetan los tipos finitos ; de esta manera, los constructores de tipos se asemejan a funciones polinómicas. [ 2 ]
Por lo tanto, el derivado de un constructor de tipos puede formarse mediante esta analogía sintáctica, y la cremallera del constructor de tipos es el derivado emparejado con su tipo de elemento. De esta manera, el derivado puede verse como una cremallera con un agujero, donde se podría colocar un valor.
Consideremos el tipo de listas enlazadas simples. La estructura de datos de la lista se puede definir como. Eso es,es un constructor de tipo que toma un tipo de elementoy produce el tipo de listas de ese tipo de elemento. Los dos sumandos corresponden a los dos constructores de datos para una lista. El Nilconstructor de datos, un nodo centinela que no contiene datos, está representado por el tipo de unidad.y el Consconstructor se representa como un producto del elemento de cabeza.y la colade la lista.
Bajo esta representación, la derivadadeen términos deesEs decir, si tenemos una lista con una cremallera y un agujero, entonces (I) el agujero es el primer elemento y el resto de la lista es una lista ordinaria (sin cremallera)., o (II) el primer elemento está presente () y el agujero está en algún otro lugar más abajo en la cola de la lista (). Entonces, el tipo formal de una cremallera para una lista enlazada es, oDicho de otro modo, una cremallera para una lista enlazada consiste en un elemento de esa lista e instrucciones sobre dónde colocarlo en una lista parcialmente construida.
Como otro ejemplo, consideremos la estructura de datos recursiva de un árbol binario con nodos que son nodos centinela de tipoo que sean hojas que contengan un valor de algún tipoPodemos representar este tipo algebraicamente como. La derivada en términos deesPodemos leer esta notación algebraica como una cremallera con un agujero: Una cremallera para un árbol tiene el "valor faltante" en la raíz misma del árbol, dejando las dos ramas como árboles ordinarios (lacaso), o tiene el valor faltante en una de las dos ramas (). En este último caso, el tipo booleanoindica qué rama (izquierda o derecha) contiene el agujero, y la cremallera contiene un valor para la raíz, unpara la rama completa y unapara la rama a la que le falta un valor de tipoEl tipo completo de una cremallera para esta estructura de árbol binario es, entonces,, o.
En general, una cremallera consta de dos partes: una colección de contextos para el nodo actual y cada uno de sus ancestros hasta el nodo raíz, y el valor que contiene el nodo actual.
Usos
La cremallera se usa a menudo cuando existe algún concepto de enfoque o un cursor que se utiliza para navegar por un conjunto de datos, ya que su semántica refleja la de moverse, pero de una manera funcional y no destructiva.
La cremallera se ha utilizado en
- Xmonad , para gestionar el enfoque y la ubicación de las ventanas.
- Los artículos de Huet tratan sobre un editor estructural [ 3 ] basado en cremalleras y un demostrador de teoremas.
- Un sistema de archivos (ZipperFS) escrito en Haskell que ofrece "...semántica transaccional; deshacer cualquier operación de archivo y directorio; instantáneas; modo de aislamiento de lectura repetible y con garantía estática para clientes; copia en escritura generalizada para archivos y directorios; función de recorrido integrada; y el comportamiento adecuado para referencias cíclicas a directorios." [ 4 ]
- Clojure tiene un amplio soporte para cremalleras. [ 5 ]
Alternativas y extensiones
Modificación directa
En un lenguaje de programación que no sea puramente funcional, puede resultar más conveniente simplemente recorrer la estructura de datos original y modificarla directamente (quizás después de clonarla profundamente , para evitar afectar a otro código que pueda contener una referencia a ella).
Cremallera genérica
La cremallera genérica [ 6 ] [ 7 ] [ 8 ] es una técnica para lograr el mismo objetivo que la cremallera convencional al capturar el estado del recorrido en una continuación mientras se visita cada nodo. (El código Haskell proporcionado en la referencia utiliza programación genérica para generar una función de recorrido para cualquier estructura de datos, pero esto es opcional ; se puede usar cualquier función de recorrido adecuada).
Sin embargo, la función de cremallera genérica implica una inversión de control , por lo que algunos usos requieren una máquina de estados (o equivalente) para realizar un seguimiento de lo que se debe hacer a continuación.
Referencias
- ↑ Huet 1997
- ↑ Joyal, André (octubre de 1981). "Una teoría combinatoria de series formales" . Avances en Matemáticas . 42 (1): 1– 82. doi : 10.1016/0001-8708(81)90052-9 .
- ↑ Hinze, Ralf; Jeuring, Johan (2001). "Functional Pearl: Weaving a web" . Journal of Functional Programming . 11 (6): 681– 689. doi : 10.1017/S0956796801004129 . ISSN 0956-7968 . S2CID 35791452 .
- ↑ Cremallera genérica: el contexto de un recorrido
- ↑ jafingerhut (22 de octubre de 2010). "clojure.zip/zipper" . ClojureDocs . Consultado el 15 de junio de 2013 .
- ↑ Chung-chieh Shan, Oleg Kiselyov (17 de agosto de 2008). "De caminar a deslizarse en cremallera, parte 1" . Recuperado el 29 de agosto de 2011 .
- ↑ Chung-chieh Shan, Oleg Kiselyov (17 de agosto de 2008). "De caminar a deslizarse en cremallera, parte 2" . Recuperado el 29 de agosto de 2011 .
- ↑ Chung-chieh Shan, Oleg Kiselyov (17 de agosto de 2008). "De caminar a deslizarse en cremallera, parte 3" . Recuperado el 29 de agosto de 2011 .
Lecturas adicionales
- Huet, Gerard (septiembre de 1997). "La Cremallera" (PDF) . Revista de programación funcional . 7 (5): 549– 554. doi : 10.1017/s0956796897002864 . S2CID 31179878 .
- Hinze, Ralf; Jeuring, Johan; Löh, Andrés (mayo de 2004). "Tipos de datos indexados por tipo" . Ciencia de la programación informática . 51 ( 1– 2): 117– 151. doi : 10.1016/j.scico.2003.07.001 .
Enlaces externos
- Cremallera
- Teseo y la cremallera
- "Crea tu propio gestor de ventanas: Controla el enfoque con una cremallera"
- Definición
- "Un grafo de flujo de control aplicativo basado en la cremallera de Huet"
- Tipos infinitesimales
- Programación funcional
- Estructuras de datos funcionales