Una cosa que se me ocurrió el otro día, son tipos específicos que aún son necesarios o un legado que nos está frenando. Lo que quiero decir es: ¿realmente necesitamos short, int, long, bigint, etc., etc.
Entiendo el razonamiento, las variables / objetos se mantienen en la memoria, la memoria debe asignarse y, por lo tanto, necesitamos saber qué tan grande puede ser una variable. Pero, en realidad, un lenguaje de programación moderno no debería ser capaz de manejar "tipos adaptativos", es decir, si algo solo se asigna en el rango abreviado, usa menos bytes, y si algo se asigna repentinamente a un número muy grande, la memoria se asigna acordemente para ese caso particular.
Flotante, real y doble son un poco más complicados ya que el tipo depende de la precisión que necesite. Sin embargo, las cadenas deberían ser capaces de ocupar menos memoria en muchos casos (en .Net) donde se usa principalmente ascii, pero las cadenas siempre ocupan el doble de memoria debido a la codificación unicode.
Un argumento para tipos específicos podría ser que es parte de la especificación, es decir, por ejemplo, una variable no debería ser mayor que un cierto valor, por lo que lo configuramos como shortint. Pero, ¿por qué no tener restricciones de tipo? Sería mucho más flexible y poderoso poder establecer rangos y valores permisibles en variables (y propiedades).
Me doy cuenta del inmenso problema de modernizar la arquitectura de tipos, ya que está tan estrechamente integrado con el hardware subyacente y cosas como la serialización pueden volverse realmente complicadas. Pero desde una perspectiva de programación, debería ser genial, ¿no?
fuente
type hour is range 0 .. 23;
Respuestas:
Creo totalmente que este es el caso. Las restricciones semánticas valen más que las restricciones de implementación. Preocuparse por el tamaño de algo se siente como preocuparse por la velocidad de algo cuando surgía la programación orientada a objetos.
No ha reemplazado la programación crítica de rendimiento. Simplemente ha hecho que la programación crítica sin rendimiento sea más productiva.
fuente
Tipos adaptativos significa lógica para hacer la adaptación, significa trabajar en tiempo de ejecución para ejecutar esa lógica (la creación de plantillas y el tiempo de compilación requerirían un tipo específico, la inferencia de tipos es un caso especial donde se obtiene lo mejor de los dos mundos). Ese trabajo adicional podría estar bien en entornos donde el rendimiento no es crítico y el sistema mantiene un tamaño razonable. En otros entornos no lo es (los sistemas embebidos son uno, donde en algún momento tiene que usar tipos enteros de 32/64 bits para el rendimiento de la CPU y tipos enteros de 8/16 bits para la optimización de la copia de seguridad de la memoria estática).
Incluso los lenguajes de uso general que admiten el enlace tardío (resolución de tipos en tiempo de ejecución, como VB6) tienden a promover una escritura fuerte ahora (VB.NET), debido al impacto en el rendimiento que solía surgir cuando se abusaba del enlace tardío, y porque a menudo terminar con un código feo cuando los tipos no son explícitos ( Referencia / Refactorización profesional en Visual Basic - Danijel Arsenovski ).
fuente
Simplicidad, memoria y velocidad Cuando declaro una variable, la memoria para esa variable se asigna en un bloque. Para admitir una variable en crecimiento dinámico, tendría que agregar el concepto de memoria no contigua a esa variable (ya sea eso o reservar el bloque más grande que la variable puede representar). La memoria no contigua reduciría el rendimiento en la asignación / recuperación. Asignar la mayor cantidad posible sería un desperdicio en el escenario en el que solo necesito un byte pero el sistema se reserva mucho tiempo.
Piense en las compensaciones entre una matriz y un vector (o una lista vinculada). Con una matriz, buscar una posición específica es una simple cuestión de obtener la posición de inicio y cambiar el puntero de memoria x espacios para ubicar esa nueva posición en la memoria. Piense en un int como un bit [32] leer un int implica recorrer esa matriz para obtener todos los valores de bit.
Para crear un tipo de número dinámico, debe cambiarlo de una matriz de bits a un vector de bits. Leer su número dinámico implica ir a la cabeza, obtener ese bit, preguntar dónde está el siguiente bit en la memoria, moverse a esa ubicación, obtener ese bit, etc. Para cada bit en el número dinámico, está haciendo tres operaciones de lectura ( actual), leer (dirección de siguiente), mover (siguiente). Imagina leer los valores de un millón de números. Eso es un millón de operaciones adicionales. Puede parecer insignificante. Pero piense en los sistemas (como los financieros) donde cada milisegundo importa.
Se tomó la decisión de que poner la responsabilidad sobre el desarrollador para verificar el tamaño y validar es una pequeña compensación en comparación con afectar el rendimiento del sistema.
fuente
Se requieren tipos específicos para lenguajes y proyectos centrados en hardware. Un ejemplo son los protocolos de red en el cable.
Pero creemos, por diversión, un tipo varint en un lenguaje como C ++. Constrúyalo a partir de una
new
variedad de entradas.No es difícil implementar la adición: solo haga una xor de los bytes juntos y verifique los bits altos: si hay una operación de acarreo,
new
en un nuevo byte superior y transfiera el bit. La resta sigue trivialmente en la representación del complemento a 2. (Esto también se conoce como sumador de transporte de ondas).La multiplicación sigue de manera similar; use la adición / desplazamiento iterativo. Como siempre, el verdadero giro en tu cola es la división [*].
¿Pero qué pierdes cuando esto sucede?
Tiempo determinista. Tiene un syscall (
new
) que puede activarse en puntos que no son necesariamente controlables.Espacio determinista.
Las matemáticas de semi-software son lentas.
Si necesita usar un lenguaje de capa de hardware y también necesita operar a un nivel alto (lento) y no quiere incorporar un motor de secuencias de comandos,
varint
tiene mucho sentido. Probablemente esté escrito en alguna parte.[*] Cf algoritmos matemáticos de hardware para formas más rápidas de hacerlo, aunque generalmente el truco son las operaciones paralelas.
fuente
Esta es una buena pregunta. Explica por qué un lenguaje como Python no necesita "short, int, long, bigint, etc.": los enteros son, bueno, enteros (hay un solo tipo entero en Python 3), y no tienen un tamaño límite (más allá de la memoria de la computadora, por supuesto).
En cuanto a Unicode, la codificación UTF-8 (que es parte de Unicode) solo usa un único carácter para los caracteres ASCII, por lo que no es tan malo.
En términos más generales, los lenguajes dinámicos parecen ir en la dirección que usted menciona. Sin embargo, por razones de eficiencia, los tipos más restringidos son útiles en algunos casos (como los programas que deben ejecutarse rápidamente). No veo muchos cambios en el futuro previsible, ya que los procesadores organizan los datos en bytes (o 2, 4, 8, etc., bytes).
fuente
Sobre la base de la teoría del lenguaje tienes razón. Los tipos deben basarse en un conjunto de estados legales, las transformaciones disponibles para esos estados y las operaciones que se realizan en esos estados.
Sin embargo, esto es más o menos lo que le ofrece la programación OOP en su forma típica. De hecho, en Java, está hablando de las clases
BigInteger
yBigDecimal
, que asignan espacio en función de la cantidad necesaria para almacenar el objeto. (Como señaló FrustratedWithFormsDesigner, muchos lenguajes de tipo secuencia de comandos están aún más lejos en este camino y ni siquiera requieren una declaración de tipo y almacenarán lo que les des).Sin embargo, el rendimiento sigue siendo relevante, y dado que es costoso cambiar los tipos en tiempo de ejecución y dado que los compiladores no pueden garantizar el tamaño máximo de una variable en tiempo de compilación, todavía tenemos variables de tamaño estático para tipos simples en muchos idiomas.
fuente
int
o unadouble
, y si no lo hace, lo saben, por lo que el dimensionamiento dinámico del valor es una característica que no necesitan pagar.Depende del idioma. Para lenguajes de nivel superior como Python, Ruby, Erlang, etc., solo tiene el concepto de números integrales y decimales.
Sin embargo, para una determinada clase de idiomas que tienen estos tipos son muy importantes. Cuando está escribiendo código para leer y escribir formatos binarios como PNG, JPeg, etc., necesita saber con precisión cuánta información se está leyendo a la vez. Lo mismo con escribir kernels del sistema operativo y controladores de dispositivos. No todos hacen esto, y en los lenguajes de nivel superior usan bibliotecas C para hacer el trabajo pesado detallado.
En
short
, todavía hay un lugar para los tipos más específicos, pero muchos problemas de desarrollo no requieren esa precisión.fuente
Recientemente creé un editor de lógica de escalera y tiempo de ejecución y decidí estar muy limitado con los tipos:
Creo que lo hizo más intuitivo para el usuario. Esta es una desviación radical de la mayoría de los PLC que tienen todo el rango "normal" de tipos que verías en un lenguaje como C.
fuente
Los lenguajes de programación se han estado moviendo en esa dirección. Tome cadenas por ejemplo. En los idiomas antiguos, debe declarar el tamaño de la cadena, como
PIC X(42)
en COBOL,DIM A$(42)
en algunas versiones de BASIC o [VAR
]CHAR(42)
en SQL. En los idiomas modernos, solo tiene unstring
tipo asignado dinámicamente y no necesita pensar en el tamaño.Sin embargo, los enteros son diferentes:
Echa un vistazo a Python. Solía distinguir entre el tamaño de máquina (
int
) y el tamaño arbitrario (long
enteros de ). En 3.x, el primero se ha ido (lo viejolong
es lo nuevoint
) y nadie se lo pierde.Pero todavía hay un tipo especializado para secuencias de enteros de 8 bits en forma de
bytes
ybytearray
. ¿Por qué no usar untuple
olist
de enteros, respectivamente? Cierto,bytes
que tiene métodos extra similares a cadenas quetuple
no, pero seguramente la eficiencia tuvo mucho que ver con eso.Realmente no. El enfoque de "todo es doble precisión" es muy común.
fuente
unum64 += ring32a-ring32b
siempre produzca el comportamiento correcto, independientemente de si el tipo entero predeterminado es de 16 bits o 64 [tenga en cuenta que el uso de+=
es esencial; una expresión comounum64a = unum64b + (ring32a-ring32b);
debería ser rechazada como ambigua.]Entiendo el razonamiento, las variables / objetos se mantienen en la memoria, la memoria debe asignarse y, por lo tanto, necesitamos saber qué tan grande puede ser una variable. Pero, en realidad, un lenguaje de programación moderno no debería ser capaz de manejar "tipos adaptativos", es decir, si algo solo se asigna en el rango abreviado, usa menos bytes, y si algo se asigna repentinamente a un número muy grande, la memoria se asigna acordemente para ese caso particular.
Flotante, real y doble son un poco más complicados ya que el tipo depende de la precisión que necesite. Sin embargo, las cadenas deberían ser capaces de ocupar menos memoria en muchos casos (en .Net) donde se usa principalmente ascii, pero las cadenas siempre ocupan el doble de memoria debido a la codificación unicode.
Fortran ha tenido algo similar (no sé si esto es exactamente lo que quiere decir, ya que realmente estoy viendo dos preguntas). Por ejemplo, en F90 hacia arriba no necesita definir explícitamente un tamaño de letra , por así decirlo. Lo cual es bueno, no solo porque le brinda un lugar central para definir sus tipos de datos, sino también una forma portátil de definirlos. REAL * 4 no es lo mismo en todas las implementaciones en todos los procesadores (y por procesador me refiero a CPU + compilador), no por una posibilidad remota.
selected_real_kind (p, r) devuelve el valor de tipo de un tipo de datos real con precisión decimal mayor de al menos p dígitos y rango de exponente mayor de al menos r.
Entonces vas, por ejemplo;
(Creo que es un ejemplo bastante autoexplicativo).
Aún no sé si entendí su pregunta correctamente, y esto es lo que usted menciona.
fuente