En informática y programación de computadoras , un tipo de datos (o simplemente tipo ) es una colección o agrupación de valores de datos, generalmente especificados por un conjunto de valores posibles, un conjunto de operaciones permitidas sobre estos valores y/o una representación de estos valores. como tipos de máquinas. [1] Una especificación de tipo de datos en un programa restringe los posibles valores que puede tomar una expresión , como una variable o una llamada a función. En datos literales, le dice al compilador o intérprete cómo el programador pretende utilizar los datos. La mayoría de los lenguajes de programación admiten tipos de datos básicos de números enteros (de distintos tamaños), números de punto flotante (que se aproximan a los números reales ), caracteres y booleanos . [2] [3]
Se puede especificar un tipo de datos por muchas razones: similitud, conveniencia o para centrar la atención. Con frecuencia es una cuestión de buena organización la que ayuda a comprender definiciones complejas. Casi todos los lenguajes de programación incluyen explícitamente la noción de tipo de datos, aunque los tipos de datos posibles suelen estar restringidos por consideraciones de simplicidad, computabilidad o regularidad. Una declaración explícita de tipo de datos normalmente permite al compilador elegir una representación de máquina eficiente, pero no se debe descartar la organización conceptual que ofrecen los tipos de datos. [4]
Diferentes idiomas pueden utilizar diferentes tipos de datos o tipos similares con diferente semántica. Por ejemplo, en el lenguaje de programación Python , int
representa un número entero de precisión arbitraria que tiene operaciones numéricas tradicionales como suma, resta y multiplicación. Sin embargo, en el lenguaje de programación Java , el tipo int
representa el conjunto de números enteros de 32 bits cuyo valor varía entre −2,147,483,648 y 2,147,483,647, con operaciones aritméticas que se ajustan en caso de desbordamiento . En Rust, se denota este tipo de entero de 32 bits y entra en pánico si se desborda en el modo de depuración. [5]i32
La mayoría de los lenguajes de programación también permiten al programador definir tipos de datos adicionales, generalmente combinando múltiples elementos de otros tipos y definiendo las operaciones válidas del nuevo tipo de datos. Por ejemplo, un programador podría crear un nuevo tipo de datos llamado " número complejo " que incluiría partes reales e imaginarias, o un tipo de datos de color representado por tres bytes que denotan las cantidades de rojo, verde y azul, y una cadena que representa el nombre del color.
Los tipos de datos se utilizan dentro de los sistemas de tipos , que ofrecen varias formas de definirlos, implementarlos y utilizarlos. En un sistema de tipos, un tipo de datos representa una restricción impuesta a la interpretación de datos, que describe la representación, interpretación y estructura de valores u objetos almacenados en la memoria de la computadora. El sistema de tipos utiliza información sobre el tipo de datos para verificar la exactitud de los programas informáticos que acceden a los datos o los manipulan. Un compilador puede utilizar el tipo estático de un valor para optimizar el almacenamiento que necesita y la elección de algoritmos para las operaciones con el valor. En muchos compiladores de C,float
el tipo de datos se representa, por ejemplo, en 32 bits , según la especificación IEEE para números de coma flotante de precisión simple . Por lo tanto, utilizarán operaciones de microprocesador específicas de coma flotante sobre esos valores (suma de coma flotante, multiplicación, etc.).
La mayoría de los tipos de datos en estadística tienen tipos comparables en programación informática y viceversa, como se muestra en la siguiente tabla:
Parnas, Shore y Weiss (1976) identificaron cinco definiciones de "tipo" que se utilizaron, a veces implícitamente, en la literatura:
La definición en términos de representación se hacía a menudo en lenguajes imperativos como ALGOL y Pascal , mientras que la definición en términos de espacio de valores y comportamiento se usaba en lenguajes de nivel superior como Simula y CLU . Los tipos que incluyen comportamiento se alinean más estrechamente con los modelos orientados a objetos , mientras que un modelo de programación estructurada tendería a no incluir código y se denominan estructuras de datos antiguas y simples .
Los tipos de datos se pueden clasificar según varios factores:
La terminología varía: en la literatura, primitivo, incorporado, básico, atómico y fundamental pueden usarse indistintamente. [8]
Todos los datos en computadoras basadas en electrónica digital se representan como bits (alternativas 0 y 1) en el nivel más bajo. La unidad de datos direccionable más pequeña suele ser un grupo de bits llamado byte (normalmente un octeto , que tiene 8 bits). La unidad procesada por instrucciones de código de máquina se llama palabra (a partir de 2011 [actualizar], [ necesita actualización ] normalmente de 32 o 64 bits).
Los tipos de datos de máquina exponen o ponen a disposición un control detallado sobre el hardware, pero esto también puede exponer detalles de implementación que hacen que el código sea menos portátil. De ahí que los tipos de máquinas se utilicen principalmente en programación de sistemas o lenguajes de programación de bajo nivel . En los lenguajes de nivel superior, la mayoría de los tipos de datos se abstraen porque no tienen una representación de máquina definida por el lenguaje. El lenguaje de programación C , por ejemplo, proporciona tipos como booleanos, enteros, números de punto flotante, etc., pero las representaciones de bits precisas de estos tipos están definidas por la implementación. El único tipo C con una representación mecánica precisa es el char
tipo que representa un byte. [9]
El tipo booleano representa los valores verdadero y falso . Aunque sólo son posibles dos valores, a menudo se representan como una palabra y no como un solo bit, ya que se requieren más instrucciones de máquina para almacenar y recuperar un bit individual. Muchos lenguajes de programación no tienen un tipo booleano explícito, sino que utilizan un tipo entero e interpretan (por ejemplo) 0 como falso y otros valores como verdadero. Los datos booleanos se refieren a la estructura lógica de cómo se interpreta el lenguaje en el lenguaje de máquina. En este caso un booleano 0 se refiere a la lógica Falso. Verdadero siempre es distinto de cero, especialmente uno que se conoce como booleano 1.
Casi todos los lenguajes de programación proporcionan uno o más tipos de datos enteros . Pueden proporcionar una pequeña cantidad de subtipos predefinidos restringidos a ciertos rangos (como short
y long
y sus variantes correspondientes unsigned
en C/C++); o permitir a los usuarios definir libremente subrangos como 1..12 (por ejemplo, Pascal / Ada ). Si no existe un tipo nativo correspondiente en la plataforma de destino, el compilador los dividirá en código utilizando los tipos que sí existen. Por ejemplo, si se solicita un entero de 32 bits en una plataforma de 16 bits, el compilador lo tratará tácitamente como una matriz de dos enteros de 16 bits.
Los tipos de datos de coma flotante representan ciertos valores fraccionarios ( números racionales , matemáticamente). Aunque tienen límites predefinidos tanto en sus valores máximos como en su precisión, a veces se les llama engañosamente reales (que evocan los números reales matemáticos ). Por lo general, se almacenan internamente en la forma a × 2 b (donde a y b son números enteros), pero se muestran en la forma decimal familiar .
Los tipos de datos de punto fijo son convenientes para representar valores monetarios. A menudo se implementan internamente como números enteros, lo que lleva a límites predefinidos.
Para independizarse de los detalles arquitectónicos, se puede proporcionar un Bignum o un tipo de precisión arbitrario numeric
. Esto representa un número entero o racional con una precisión limitada únicamente por la memoria disponible y los recursos computacionales en el sistema. Las implementaciones de Bignum de operaciones aritméticas en valores del tamaño de una máquina son significativamente más lentas que las operaciones de máquina correspondientes. [10]
El tipo enumerado tiene valores distintos, que pueden compararse y asignarse, pero que no necesariamente tienen ninguna representación concreta en particular en la memoria de la computadora; los compiladores e intérpretes pueden representarlos arbitrariamente. Por ejemplo, los cuatro palos de una baraja de naipes pueden ser cuatro enumeradores denominados CLUB , DIAMANTE , CORAZÓN , ESPADA , pertenecientes a un tipo enumerado denominado palo . Si se declara una variable V que tiene traje como tipo de datos, se le puede asignar cualquiera de esos cuatro valores. Algunas implementaciones permiten a los programadores asignar valores enteros a los valores de enumeración, o incluso tratarlos como un tipo equivalente a los números enteros.
Las cadenas son una secuencia de caracteres que se utilizan para almacenar palabras o texto sin formato , la mayoría de las veces lenguajes de marcado textuales que representan texto formateado . Los caracteres pueden ser una letra de algún alfabeto , un dígito, un espacio en blanco, un signo de puntuación, etc. Los caracteres se extraen de un conjunto de caracteres como ASCII . Los tipos de caracteres y cadenas pueden tener diferentes subtipos según la codificación de caracteres. Se descubrió que el ASCII original de 7 bits de ancho era limitado y fue reemplazado por conjuntos de 8, 16 y 32 bits, que pueden codificar una amplia variedad de alfabetos no latinos (como el hebreo y el chino ) y otros símbolos. Las cadenas pueden ser de longitud variable o fija, y algunos lenguajes de programación tienen ambos tipos. También podrán subtipificarse por su tamaño máximo.
Dado que la mayoría de los juegos de caracteres incluyen dígitos , es posible tener una cadena numérica, como por ejemplo "1234"
. Estas cadenas numéricas generalmente se consideran distintas de los valores numéricos como 1234
, aunque algunos idiomas realizan conversiones automáticamente entre ellos.
Una definición de tipo de unión especificará cuál de varios subtipos permitidos puede almacenarse en sus instancias, por ejemplo, "flotante o entero largo". A diferencia de un registro , que podría definirse para contener un flotante y un número entero, una unión sólo puede contener un subtipo a la vez.
Una unión etiquetada (también denominada variante , registro de variante, unión discriminada o unión disjunta) contiene un campo adicional que indica su tipo actual para mejorar la seguridad de tipos.
Un tipo de datos algebraico (ADT) es un tipo de suma posiblemente recursiva de tipos de productos . Un valor de un ADT consta de una etiqueta de constructor junto con cero o más valores de campo, con el número y tipo de valores de campo fijados por el constructor. El conjunto de todos los valores posibles de un TDA es la unión disjunta (suma) teórica de conjuntos de los conjuntos de todos los valores posibles de sus variantes (producto de campos). Los valores de tipos algebraicos se analizan con coincidencia de patrones, que identifica el constructor de un valor y extrae los campos que contiene.
Si solo hay un constructor, entonces el ADT corresponde a un tipo de producto similar a una tupla o registro. Un constructor sin campos corresponde al producto vacío (tipo de unidad). Si ninguno de los constructores tiene campos, entonces el ADT corresponde a un tipo enumerado .
Un ADT común es el tipo de opción , definido en Haskell como . [11]data Maybe a = Nothing | Just a
Algunos tipos son muy útiles para almacenar y recuperar datos y se denominan estructuras de datos . Las estructuras de datos comunes incluyen:
data List a = Nil | Cons a (List a)
data BTree a = Nil | Node (BTree a) a (BTree a)
Un tipo de datos abstracto es un tipo de datos que no especifica la representación concreta de los datos. En cambio, para describirlo se utiliza una especificación formal basada en las operaciones del tipo de datos. Cualquier implementación de una especificación debe cumplir las reglas dadas. Por ejemplo, una pila tiene operaciones push/pop que siguen una regla de último en entrar, primero en salir y se puede implementar concretamente utilizando una lista o una matriz. Los tipos de datos abstractos se utilizan en semántica formal y verificación de programas y, de manera menos estricta, en diseño .
El principal tipo derivado no compuesto es el puntero , un tipo de datos cuyo valor se refiere directamente a (o "apunta") a otro valor almacenado en otra parte de la memoria de la computadora usando su dirección . Es un tipo primitivo de referencia . (En términos cotidianos, un número de página de un libro podría considerarse un dato que remite a otro). Los punteros suelen almacenarse en un formato similar a un número entero; sin embargo, intentar eliminar la referencia o "buscar" un puntero cuyo valor nunca fue una dirección de memoria válida provocaría que el programa fallara. Para mejorar este problema potencial, los punteros se consideran un tipo separado del tipo de datos al que apuntan, incluso si la representación subyacente es la misma.
Los lenguajes de programación funcional tratan las funciones como un tipo de datos distinto y permiten que los valores de este tipo se almacenen en variables y se pasen a funciones. Algunos lenguajes multiparadigma, como JavaScript , también tienen mecanismos para tratar funciones como datos. [13] La mayoría de los sistemas de tipos contemporáneos van más allá del simple tipo "objeto de función" de JavaScript y tienen una familia de tipos de funciones diferenciadas por argumentos y tipos de retorno, como el tipo Int -> Bool
que denota funciones que toman un número entero y devuelven un booleano. En C, una función no es un tipo de datos de primera clase, pero el programa puede manipular los punteros de función . Java y C++ originalmente no tenían valores de función, pero los agregaron en C++ 11 y Java 8.
Un constructor de tipos construye nuevos tipos a partir de los antiguos y puede considerarse como un operador que toma cero o más tipos como argumentos y produce un tipo. Los tipos de productos, tipos de funciones, tipos de potencia y tipos de listas se pueden convertir en constructores de tipos.
Los tipos cuantificados universalmente y existencialmente se basan en la lógica de predicados . La cuantificación universal se escribe como o y es la intersección de todos los tipos de cuerpo , es decir, el valor es de tipo para cada . La cuantificación existencial escrita como o y es la unión entre todos los tipos de cuerpo , es decir, el valor es de tipo para algunos .forall x. f x
x
f x
f x
x
exists x. f x
x
f x
f x
x
En Haskell, la cuantificación universal se usa comúnmente, pero los tipos existenciales deben codificarse transformándose exists a. f a
a forall r. (forall a. f a -> r) -> r
un tipo similar.
Un tipo de refinamiento es un tipo dotado de un predicado que se supone válido para cualquier elemento del tipo refinado. Por ejemplo, el tipo de números naturales mayores que 5 se puede escribir como
Un tipo dependiente es un tipo cuya definición depende de un valor. Dos ejemplos comunes de tipos dependientes son las funciones dependientes y los pares dependientes. El tipo de retorno de una función dependiente puede depender del valor (no sólo del tipo) de uno de sus argumentos. Un par dependiente puede tener un segundo valor cuyo tipo depende del primer valor.
Un tipo de intersección es un tipo que contiene aquellos valores que son miembros de dos tipos específicos. Por ejemplo, en Java la clase Boolean
implementa tanto las interfaces Serializable
como Comparable
. Por lo tanto, un objeto de tipo Boolean
es miembro del tipo Serializable & Comparable
. Considerando los tipos como conjuntos de valores, el tipo de intersección es la intersección teórica de conjuntos de y . También es posible definir un tipo de intersección dependiente, denominado , donde el tipo puede depender del término variable . [14]
Algunos lenguajes de programación representan la información de tipo como datos, lo que permite la introspección y reflexión de tipos . Por el contrario, los sistemas de tipos de orden superior , si bien permiten que los tipos se construyan a partir de otros tipos y se pasen a funciones como valores, normalmente evitan basar las decisiones computacionales en ellos. [ cita necesaria ]
Para mayor comodidad, los lenguajes de alto nivel y las bases de datos pueden proporcionar tipos de datos del "mundo real" ya preparados, por ejemplo horas, fechas y valores monetarios (moneda). [15] [16] Estos pueden estar integrados en el lenguaje o implementarse como tipos compuestos en una biblioteca. [17]
Cuál de [signo y magnitud, complemento a dos, complemento a uno] se aplica está definido por la implementación
Hace algún tiempo describí MONEY como un tipo de datos "conveniencia" que es efectivamente lo mismo que DECIMAL(19,4), [...]
{{cite book}}
: CS1 maint: location missing publisher (link)