En informática , un tipo de dato abstracto ( TDA ) es un modelo matemático para tipos de datos , definido por su comportamiento ( semántica ) desde el punto de vista del usuario , específicamente en términos de valores posibles, operaciones posibles sobre datos de este tipo y el comportamiento de dichas operaciones. Este modelo matemático contrasta con las estructuras de datos , que son representaciones concretas de los datos y representan el punto de vista del implementador, no del usuario. Por ejemplo, una pila tiene operaciones de inserción/extracción que siguen la regla Último en Entrar, Primero en Salir (LIFO) y puede implementarse concretamente mediante una lista enlazada o un array. Otro ejemplo es un conjunto , que almacena valores sin un orden específico y sin valores repetidos. Los valores en sí no se recuperan de los conjuntos; en cambio, se comprueba si un valor pertenece a un conjunto para obtener un valor booleano "pertenece" o "no pertenece".
Los TAD son un concepto teórico, utilizado en semántica formal y verificación de programas y, de forma menos estricta, en el diseño y análisis de algoritmos , estructuras de datos y sistemas de software . La mayoría de los lenguajes de programación convencionales no admiten directamente la especificación formal de TAD. Sin embargo, diversas características de los lenguajes de programación se corresponden con ciertos aspectos de la implementación de TAD y a menudo se confunden con los propios TAD; estas incluyen tipos abstractos , tipos de datos opacos , protocolos y diseño por contrato . Por ejemplo, en la programación modular , el módulo declara procedimientos que se corresponden con las operaciones del TAD, a menudo con comentarios que describen las restricciones. Esta estrategia de ocultación de información permite modificar la implementación del módulo sin afectar a los programas cliente , pero el módulo solo define informalmente un TAD. La noción de tipos de datos abstractos está relacionada con el concepto de abstracción de datos , importante en la programación orientada a objetos y en las metodologías de diseño por contrato para la ingeniería de software . [ 1 ]
Historia
Los ADT fueron propuestos por primera vez por Barbara Liskov y Stephen N. Zilles en 1974, como parte del desarrollo del lenguaje CLU . [ 2 ] La especificación algebraica fue un tema importante de investigación en CS alrededor de 1980 y casi un sinónimo de tipos de datos abstractos en ese momento. [ 3 ] Tiene una base matemática en el álgebra universal . [ 4 ]
Definición
Formalmente, un TAD es análogo a una estructura algebraica en matemáticas, [ 5 ] que consta de un dominio, una colección de operaciones y un conjunto de restricciones que las operaciones deben satisfacer. [ 6 ] El dominio se define a menudo implícitamente, por ejemplo, el objeto libre sobre el conjunto de operaciones del TAD. La interfaz del TAD normalmente se refiere solo al dominio y a las operaciones, y quizás a algunas de las restricciones sobre las operaciones, como las precondiciones y las postcondiciones; pero no a otras restricciones, como las relaciones entre las operaciones, que se consideran comportamiento. Existen dos estilos principales de especificaciones formales para el comportamiento: la semántica axiomática y la semántica operacional . [ 7 ]
Aunque no forman parte de la interfaz, las restricciones siguen siendo importantes para la definición del TAD; por ejemplo, una pila y una cola tienen interfaces similares para agregar/eliminar elementos, pero son las restricciones las que distinguen el comportamiento último en entrar, primero en salir del comportamiento primero en entrar, primero en salir. Las restricciones no consisten solo en ecuaciones, sino fetch(store(S,v))=vtambién en fórmulas lógicas .
semántica axiomática
En el espíritu de la programación funcional , cada estado de una estructura de datos abstracta (TDA) es una entidad o valor independiente. Desde esta perspectiva, cada operación se modela como una función matemática sin efectos secundarios . Las operaciones que modifican la TDA se modelan como funciones que toman el estado anterior como argumento y devuelven el nuevo estado como parte del resultado. El orden en que se evalúan las operaciones es irrelevante, y la misma operación aplicada a los mismos argumentos (incluidos los mismos estados de entrada) siempre devolverá los mismos resultados (y estados de salida). Las restricciones se especifican como axiomas o leyes algebraicas que las operaciones deben satisfacer.
Semántica operacional
En el espíritu de la programación imperativa , una estructura de datos abstracta se concibe como una entidad mutable , lo que implica que existe una noción de tiempo y que la TDA puede encontrarse en diferentes estados en distintos momentos. Las operaciones modifican el estado de la TDA a lo largo del tiempo; por lo tanto, el orden en que se evalúan las operaciones es importante, y la misma operación sobre las mismas entidades puede tener efectos diferentes si se ejecuta en momentos distintos. Esto es análogo a las instrucciones de un ordenador o a los comandos y procedimientos de un lenguaje imperativo. Para enfatizar esta perspectiva, se suele decir que las operaciones se ejecutan o aplican , en lugar de evaluarse , de forma similar al estilo imperativo que se utiliza a menudo al describir algoritmos abstractos. Las restricciones se especifican normalmente en prosa.
Operaciones auxiliares
Las presentaciones de ADT suelen limitarse a las operaciones clave. Las presentaciones más completas suelen especificar operaciones auxiliares en los ADT, tales como:
create(), que produce una nueva instancia del TAD;compare(s, t), que comprueba si los estados de dos instancias son equivalentes en algún sentido;hash(s), que calcula alguna función hash estándar a partir del estado de la instancia;print(s)o bienshow(s), que produce una representación legible para humanos del estado de la instancia.
Estos nombres son ilustrativos y pueden variar entre autores. En las definiciones de TAD de estilo imperativo, también se suele encontrar:
initialize(s), que prepara una instancia recién creadaspara operaciones posteriores, o la restablece a algún "estado inicial";copy(s), que coloca la instanciasen un estado equivalente al det;clone(t), que realizas←create(),copy(s, t), y devuelves;free(s)odestroy(s), que recupera la memoria y otros recursos utilizados pors.
La freeoperación normalmente no es relevante ni significativa, ya que los TAD son entidades teóricas que no "usan memoria". Sin embargo, puede ser necesaria cuando se necesita analizar el almacenamiento utilizado por un algoritmo que utiliza el TAD. En ese caso, se necesitan axiomas adicionales que especifiquen cuánta memoria usa cada instancia de TAD, en función de su estado, y cuánta de ella se devuelve al pool free.
Tipos restringidos
La definición de un tipo de dato abstracto (TDA) suele restringir los valores almacenados para sus instancias a elementos de un conjunto específico X, denominado rango de dichas variables. Por ejemplo, una variable abstracta puede estar restringida a almacenar únicamente números enteros. Al igual que en los lenguajes de programación, estas restricciones pueden simplificar la descripción y el análisis de algoritmos , y mejorar su legibilidad.
Aliasing
En el estilo operacional, a menudo no queda claro cómo se manejan las múltiples instancias ni si la modificación de una instancia puede afectar a las demás. Un estilo común de definición de TAD consiste en escribir las operaciones como si solo existiera una instancia durante la ejecución del algoritmo, y todas las operaciones se aplicaran a esa instancia. Por ejemplo, una pila puede tener operaciones push( x ) y pop(), que operan sobre la única pila existente. Las definiciones de TAD en este estilo se pueden reescribir fácilmente para admitir múltiples instancias coexistentes del TAD, añadiendo un parámetro de instancia explícito (como S en el ejemplo de la pila que se muestra a continuación) a cada operación que utilice o modifique la instancia implícita. Algunos TAD no se pueden definir de forma significativa sin permitir múltiples instancias, por ejemplo, cuando una sola operación toma dos instancias distintas del TAD como parámetros, como una unionoperación sobre conjuntos o una compareoperación sobre listas.
El estilo de instancias múltiples a veces se combina con un axioma de alias , a saber, que el resultado de create() es distinto de cualquier instancia que ya esté siendo utilizada por el algoritmo. Las implementaciones de TAD aún pueden reutilizar memoria y permitir que las implementaciones de create() generen una instancia creada previamente; sin embargo, definir que dicha instancia sea "reutilizada" es difícil en el formalismo de los TAD.
En términos más generales, este axioma puede reforzarse para excluir también el aliasing parcial con otras instancias, de modo que los TAD compuestos (como árboles o registros) y los TAD de estilo referencial (como punteros) pueden considerarse completamente disjuntos. Por ejemplo, al extender la definición de una variable abstracta para incluir registros abstractos , las operaciones sobre un campo F de una variable de registro R involucran claramente a F , que es distinto de R , pero también forma parte de ella. Un axioma de aliasing parcial establecería que cambiar un campo de una variable de registro no afecta a ningún otro registro.
Análisis de complejidad
Algunos autores también incluyen la complejidad computacional ("costo") de cada operación, tanto en términos de tiempo (para operaciones de cálculo) como de espacio (para representar valores), para facilitar el análisis de algoritmos . Por ejemplo, se puede especificar que cada operación requiere el mismo tiempo y cada valor el mismo espacio, independientemente del estado del TAD, o que existe un "tamaño" del TAD y que las operaciones son lineales, cuadráticas, etc., en función del tamaño del TAD. Alexander Stepanov , diseñador de la Biblioteca de Plantillas Estándar de C++ , incluyó garantías de complejidad en la especificación de la STL, argumentando:
La razón para introducir el concepto de tipos de datos abstractos fue permitir la intercambiabilidad de módulos de software. No se pueden tener módulos intercambiables a menos que compartan un comportamiento de complejidad similar. Si reemplazo un módulo por otro con el mismo comportamiento funcional pero con diferentes compensaciones de complejidad, el usuario de este código se llevará una desagradable sorpresa. Podría explicarle todo lo que quisiera sobre la abstracción de datos, y aun así no querría usar el código. Las afirmaciones de complejidad deben formar parte de la interfaz.
— Alexander Stepanov [ 8 ]
Otros autores discrepan, argumentando que un tipo de dato abstracto (TDA) de pila es el mismo tanto si se implementa con una lista enlazada como con un array, a pesar de la diferencia en los costes operativos, y que la especificación de un TDA debería ser independiente de la implementación.
Ejemplos
Variable abstracta
Una variable abstracta puede considerarse el TAD no trivial más simple, con la semántica de una variable imperativa. Admite dos operaciones, fetchy store. Las definiciones operacionales a menudo se escriben en términos de variables abstractas. En la semántica axiomática, dejandoser el tipo de la variable abstracta ysea el tipo de su contenido, fetches una funcióny storees una función de tipo. La restricción principal es que fetchsiempre devuelva el valor xstore utilizado en la operación más reciente sobre la misma variable V , es decir fetch(store(V,x)) = x. También podemos requerir que storesobrescriba el valor por completo, store(store(V,x1),x2) = store(V,x2).
En la semántica operacional, fetch( V ) es un procedimiento que devuelve el valor actual en la ubicación V , y store( V , x ) es un procedimiento con voidtipo de retorno que almacena el valor x en la ubicación V. Las restricciones se describen informalmente como que las lecturas son consistentes con las escrituras. Como en muchos lenguajes de programación, la operación store( V , x ) se suele escribir V ← x (o alguna notación similar), y fetch( V ) está implícito siempre que se usa una variable V en un contexto donde se requiere un valor. Así, por ejemplo, V ← V + 1 se entiende comúnmente como una abreviatura de store( V , fetch( V ) + 1).
En esta definición, se asume implícitamente que los nombres son siempre distintos: almacenar un valor en una variable U no tiene efecto sobre el estado de una variable distinta V. Para hacer explícita esta suposición, se podría agregar la restricción de que:
- Si U y V son variables distintas, la secuencia {
store( U , x );store( V , y ) } es equivalente a {store( V , y );store( U , x ) }.
Esta definición no dice nada sobre el resultado de evaluar fetch( V ) cuando V no está inicializado , es decir, antes de realizar cualquier storeoperación sobre V. La obtención de datos antes de su almacenamiento puede estar prohibida, definida para tener un resultado determinado o quedar sin especificar. Hay algunos algoritmos cuya eficiencia depende de la suposición de que tal operación fetches válida y devuelve algún valor arbitrario dentro del rango de la variable.
Pila abstracta
Una pila abstracta es una estructura de último en entrar, primero en salir. Generalmente se define mediante tres operaciones clave: pushinsertar un elemento de datos en la pila; popeliminar un elemento de datos; y peekacceder topa un elemento de datos en la parte superior de la pila sin eliminarlo. Una definición completa de pila abstracta incluye también una función booleanaempty ( S ) y una createoperación () que devuelve una instancia inicial de la pila.
En la semántica axiomática, dejandoser el tipo de estados de pila ysean el tipo de valores contenidos en la pila, estos podrían tener los tipos,,,, yEn la semántica axiomática, crear la pila inicial es una operación "trivial" y siempre devuelve el mismo estado distinguido. Por lo tanto, a menudo se designa con un símbolo especial como Λ o "()". El emptypredicado de la operación se puede escribir entonces simplemente comoo.
Las restricciones son entonces pop(push(S,v))=(S,v), top(push(S,v))=v, [ 9 ]empty ( create) = T (una pila recién creada está vacía), empty( push( S , x )) = F (empujar algo a una pila la hace no vacía). Estos axiomas no definen el efecto de top( s ) o pop( s ), a menos que s sea un estado de pila devuelto por un push. Dado que pushdeja la pila no vacía, esas dos operaciones pueden definirse como inválidas cuando s = Λ. De estos axiomas (y la falta de efectos secundarios), se puede deducir que push(Λ, x ) ≠ Λ. Además, push( s , x ) = push( t , y ) si y solo si x = y y s = t .
Como en otras ramas de las matemáticas, es habitual suponer que los estados de la pila son solo aquellos cuya existencia puede probarse a partir de los axiomas en un número finito de pasos. En este caso, significa que cada pila es una secuencia finita de valores que se convierte en la pila vacía (Λ) tras un número finito de iteraciones ( pops). Por sí mismos, los axiomas anteriores no excluyen la existencia de pilas infinitas (que pueden popiterarse indefinidamente, generando cada vez un estado diferente) ni de pilas circulares (que vuelven al mismo estado tras un número finito de popiteraciones). En particular, no excluyen estados s tales que pop( s ) = s o push( s , x ) = s para algún x . Sin embargo, dado que no se pueden obtener tales estados de pila a partir del estado inicial con las operaciones dadas, se supone que "no existen".
En la definición operacional de una pila abstracta, push( S , x ) no devuelve nada y pop( S ) produce el valor como resultado, pero no el nuevo estado de la pila. Existe entonces la restricción de que, para cualquier valor x y cualquier variable abstracta V , la secuencia de operaciones { push( S , x ); V ← pop( S )} es equivalente a V ← x . Dado que la asignación V ← x , por definición, no puede cambiar el estado de S , esta condición implica que V ← pop( S ) restaura S al estado que tenía antes de push( S , x ). De esta condición y de las propiedades de las variables abstractas, se deduce, por ejemplo, que la secuencia:
- {
push( S , x );push( S , y ); U ←pop( S );push( S , z ); V ←pop( S ); W ←pop( S ) }
donde x , y y z son valores cualesquiera, y U , V , W son variables distintas entre sí, es equivalente a:
- { U ← y ; V ← z ; W ← x }
A diferencia de la semántica axiomática, la semántica operacional puede sufrir de aliasing. Aquí se asume implícitamente que las operaciones en una instancia de pila no modifican el estado de ninguna otra instancia de TAD, incluidas otras pilas; es decir:
- Para cualesquiera valores x , y , y cualesquiera pilas distintas S y T , la secuencia {
push( S , x );push( T , y ) } es equivalente a {push( T , y );push( S , x ) }.
Jerarquía del auge
Un ejemplo más complejo es la jerarquía Boom de los tipos de datos abstractos árbol binario , lista , bolsa y conjunto . [ 10 ] Todos estos tipos de datos se pueden declarar mediante tres operaciones: null , que construye el contenedor vacío; single , que construye un contenedor a partir de un solo elemento; y append , que combina dos contenedores del mismo tipo. La especificación completa para los cuatro tipos de datos se puede dar agregando sucesivamente las siguientes reglas sobre estas operaciones:
El acceso a los datos se puede especificar mediante la coincidencia de patrones en las tres operaciones, por ejemplo, una función miembro para estos contenedores mediante:
Es necesario asegurarse de que la función sea invariante según las reglas pertinentes para el tipo de dato. Dentro de cada una de las clases de equivalencia implícitas en el subconjunto de ecuaciones elegido, debe producir el mismo resultado para todos sus miembros.
ADT comunes
Algunos tipos de datos abstractos (TDA) comunes, que han demostrado ser útiles en una gran variedad de aplicaciones, son:
Cada uno de estos tipos de datos abstractos (TDA) puede definirse de muchas maneras y variantes, no necesariamente equivalentes. Por ejemplo, una pila abstracta puede o no tener una countoperación que indique cuántos elementos se han insertado y aún no se han extraído. Esta elección tiene repercusiones no solo para los clientes, sino también para la implementación.
- Tipo de datos gráficos abstractos
En 1979 se propuso una extensión de ADT para gráficos por computadora: [ 11 ] un tipo de datos gráficos abstractos (AGDT). Fue introducido por Nadia Magnenat Thalmann y Daniel Thalmann . Los AGDT ofrecen las ventajas de los ADT con facilidades para construir objetos gráficos de manera estructurada.
Implementación
Los tipos de datos abstractos son entidades teóricas que se utilizan (entre otras cosas) para simplificar la descripción de algoritmos abstractos, clasificar y evaluar estructuras de datos y describir formalmente los sistemas de tipos de los lenguajes de programación. Sin embargo, un TDA puede implementarse . Esto significa que cada instancia o estado del TDA está representado por algún tipo de dato o estructura de datos concretos , y para cada operación abstracta existe un procedimiento o función correspondiente . Estos procedimientos implementados satisfacen las especificaciones y axiomas del TDA hasta cierto punto. En la práctica, la implementación no es perfecta, y los usuarios deben ser conscientes de los problemas derivados de las limitaciones de la representación y los procedimientos implementados.
Por ejemplo, los enteros pueden especificarse como un tipo de dato abstracto (TDA), definido por los valores 0 y 1, y las operaciones de suma, resta, multiplicación, división (con especial atención a la división por cero), comparación, etc., comportándose según los axiomas matemáticos habituales del álgebra abstracta, como la asociatividad, la conmutatividad, etc. Sin embargo, en un ordenador, los enteros se representan más comúnmente como números binarios de 32 o 64 bits de ancho fijo . Los usuarios deben tener en cuenta los problemas que presenta esta representación, como el desbordamiento aritmético , donde el TDA especifica un resultado válido, pero la representación no puede contener dicho valor. No obstante, para muchos fines, el usuario puede ignorar estas limitaciones y simplemente utilizar la implementación como si se tratara del tipo de dato abstracto.
Por lo general, existen muchas maneras de implementar un mismo TAD, utilizando diversas estructuras de datos concretas. Así, por ejemplo, una pila abstracta puede implementarse mediante una lista enlazada o un array . Las distintas implementaciones del TAD, que poseen las mismas propiedades y capacidades, pueden considerarse semánticamente equivalentes y utilizarse indistintamente en el código que emplea el TAD. Esto proporciona una forma de abstracción o encapsulación, y ofrece una gran flexibilidad al utilizar objetos TAD en diferentes situaciones. Por ejemplo, distintas implementaciones del TAD pueden ser más eficientes en diferentes contextos; es posible utilizar cada una según su conveniencia, aumentando así la eficiencia general. El código que utiliza una implementación del TAD según su interfaz seguirá funcionando incluso si se modifica dicha implementación.
Para evitar que los clientes dependan de la implementación, un TAD suele empaquetarse como un tipo de dato opaco o un identificador de algún tipo [ 12 ] en uno o más módulos , cuya interfaz contiene únicamente la firma (número y tipos de los parámetros y resultados) de las operaciones. La implementación del módulo —es decir, los cuerpos de los procedimientos y la estructura de datos concreta utilizada— puede ocultarse a la mayoría de los clientes del módulo. Esto permite modificar la implementación sin afectar a los clientes. Si la implementación se expone, se la conoce como un tipo de dato transparente.
Los lenguajes modernos orientados a objetos, como C++ y Java , admiten una forma de tipos de datos abstractos (TDA). Cuando una clase se usa como tipo, se trata de un tipo abstracto que hace referencia a una representación oculta. En este modelo, un TDA se implementa típicamente como una clase , y cada instancia del TDA suele ser un objeto de esa clase. La interfaz del módulo normalmente declara los constructores como procedimientos ordinarios, y la mayoría de las demás operaciones del TDA como métodos de esa clase. Muchos lenguajes de programación modernos, como C++ y Java, incluyen bibliotecas estándar que implementan numerosos TDA con este estilo. Sin embargo, este enfoque no encapsula fácilmente las múltiples variantes de representación que se encuentran en un TDA. También puede perjudicar la extensibilidad de los programas orientados a objetos. En un programa puramente orientado a objetos que usa interfaces como tipos, los tipos hacen referencia a comportamientos, no a representaciones.
La especificación de algunos lenguajes de programación es intencionadamente vaga respecto a la representación de ciertos tipos de datos integrados, definiendo únicamente las operaciones que se pueden realizar sobre ellos. Por lo tanto, estos tipos pueden considerarse como "tipos de datos abstractos integrados". Un ejemplo de ello son los arrays en muchos lenguajes de scripting, como Awk , Lua y Perl , que pueden considerarse una implementación de la lista abstracta.
En un lenguaje de especificación formal , los tipos de datos abstractos (TDA) se pueden definir axiomáticamente, y el lenguaje permite manipular sus valores, lo que proporciona una implementación directa e inmediata. La familia de lenguajes de programación OBJ , por ejemplo, permite definir ecuaciones para su especificación y reescribirlas para ejecutarlas. Sin embargo, estas implementaciones automáticas no suelen ser tan eficientes como las implementaciones dedicadas.
Ejemplo: implementación de la pila abstracta
Como ejemplo, aquí hay una implementación de la pila abstracta anterior en el lenguaje de programación C.
Interfaz de estilo imperativo
Una interfaz de estilo imperativo podría ser:
// tipo: representación de instancia de pila (registro opaco) typedef struct { // implementación aquí } Pila ;// tipo: valor almacenado en la instancia de la pila (dirección arbitraria) typedef void * Item ;// crea una nueva instancia de pila vacía Stack * stack_create ( void );// agrega un elemento en la parte superior de la pila void stack_push ( Stack * s , Item x );// elimina el elemento superior de la pila y lo devuelve Item stack_pop ( Stack * s );// Comprueba si la pila está vacía bool stack_is_empty ( Stack * s );Esta interfaz podría utilizarse de la siguiente manera:
#include <stack.h>int main () { Stack * s = stack_create (); // crea una nueva instancia de pila vacía int x = 17 ;// agrega la dirección de x en la parte superior de la pila stack_push ( s , & x ); // elimina la dirección de x de la pila y la devuelve Item y = stack_pop ( s );if ( stack_is_empty ( s )) { // hace algo si la pila está vacía printf ( "¡La pila está vacía!" ); } }Esta interfaz puede implementarse de muchas maneras. La implementación puede ser arbitrariamente ineficiente, ya que la definición formal del TAD, arriba, no especifica cuánto espacio puede usar la pila, ni cuánto tiempo debe tomar cada operación. Tampoco especifica si el estado de la pila scontinúa existiendo después de una llamada x← pop(s).
En la práctica, la definición formal debería especificar que el espacio es proporcional al número de elementos insertados y aún no extraídos; y que cada una de las operaciones anteriores debe finalizar en un tiempo constante, independientemente de dicho número. Para cumplir con estas especificaciones adicionales, la implementación podría utilizar una lista enlazada o un arreglo (con redimensionamiento dinámico) junto con dos números enteros (el número de elementos y el tamaño del arreglo).
Interfaz de estilo funcional
Las definiciones de tipos de datos algebraicos (TDA) de estilo funcional son más apropiadas para lenguajes de programación funcional, y viceversa. Sin embargo, se puede proporcionar una interfaz de estilo funcional incluso en un lenguaje imperativo como C. Por ejemplo:
// tipo: representación de instancia de pila (registro opaco) typedef struct { // implementación aquí } Pila ;// tipo: valor almacenado en la instancia de la pila (dirección arbitraria) typedef void * Item ;// Devuelve el estado de pila vacía Stack * stack_is_empty ( void );// agrega un elemento en la parte superior del estado de la pila y devuelve el estado de la pila resultante Stack * stack_push ( Stack * s , Item x );// elimina el elemento superior del estado de la pila y devuelve el estado de pila resultante Stack * stack_pop ( Stack * s );// devuelve el elemento superior del estado de la pila Item stack_top ( Stack * s );Véase también
Citas
- ↑ "Lectura 10: Tipos de datos abstractos" . MIT.
- ↑ Liskov y Zilles 1974 .
- ↑ Ehrig, H. (1985). Fundamentos de la especificación algebraica 1 - Ecuaciones y semántica inicial . Springer-Verlag. ISBN 0-387-13718-1.
- ↑ Wechler, Wolfgang (1992). Álgebra universal para informáticos . Springer-Verlag. ISBN 0-387-54280-9.
- ↑ Rudolf Lidl (2004). Álgebra abstracta . Springer. ISBN 978-81-8128-149-4., Capítulo 7, sección 40.
- ↑ Dale y Walker 1996 , pág. 3.
- ↑ Dale y Walker 1996 , pág. 4.
- ↑ Stevens, Al (marzo de 1995). "Al Stevens entrevista a Alex Stepanov" . Dr. Dobb's Journal . Consultado el 31 de enero de 2015 .
- ↑ Black, Paul E. (24 de agosto de 2005). "semántica axiomática" . Diccionario de algoritmos y estructuras de datos . Recuperado el 25 de noviembre de 2023 .
- ↑ Bunkenburg, Alexander (1994). "The Boom Hierarchy". Functional Programming, Glasgow 1993. Workshops in Computing. pp. 1–8 . CiteSeerX 10.1.1.49.3252 . doi : 10.1007/978-1-4471-3236-3_1 . ISBN 978-3-540-19879-6.
- ↑ D. Thalmann, N. Magnenat Thalmann (1979). Diseño e implementación de tipos de datos gráficos abstractos . IEEE. doi : 10.1109/CMPSAC.1979.762551 ., Actas de la 3ª Conferencia Internacional sobre Software y Aplicaciones Informáticas (COMPSAC'79), IEEE, Chicago, EE. UU., págs. 519-524
- ↑ Robert Sedgewick (1998). Algoritmos en C. Addison/Wesley. ISBN 978-0-201-31452-6., definición 4.4.
Referencias
- Liskov, Barbara ; Zilles, Stephen (1974). "Programación con tipos de datos abstractos". Actas del Simposio ACM SIGPLAN sobre Lenguajes de Muy Alto Nivel . SIGPLAN Notices. Vol. 9. págs. 50–59 . CiteSeerX 10.1.1.136.3043 . doi : 10.1145/800233.807045 .
- Dale, Nell; Walker, Henry M. (1996). Tipos de datos abstractos: especificaciones, implementaciones y aplicaciones . Jones & Bartlett Learning. ISBN 978-0-66940000-7.
Lecturas adicionales
- Mitchell, John C.; Plotkin , Gordon (julio de 1988). "Los tipos abstractos tienen tipo existencial" (PDF) . ACM Transactions on Programming Languages and Systems . 10 (3): 470– 502. doi : 10.1145/44501.45065 . S2CID 1222153. Archivado (PDF) del original el 9 de octubre de 2022 .
Enlaces externos
Contenido multimedia relacionado con los tipos de datos abstractos en Wikimedia Commons.- Tipo de dato abstracto en el Diccionario de Algoritmos y Estructuras de Datos del NIST.
- Tipos de datos abstractos
- Tipos de datos
- teoría de tipos