Uma coisa que me ocorreu no outro dia, são tipos específicos ainda necessários ou um legado que está nos impedindo. O que quero dizer é: realmente precisamos de short, int, long, bigint etc etc.
Eu entendo o raciocínio, variáveis / objetos são mantidos na memória, a memória precisa ser alocada e, portanto, precisamos saber quão grande pode ser uma variável. Mas, realmente, uma linguagem de programação moderna não deveria ser capaz de lidar com "tipos adaptativos", isto é, se algo é alocado apenas no intervalo curto, ele usa menos bytes e se algo é alocado repentinamente em um número muito grande, a memória é alocada de acordo com essa instância em particular.
Flutuação, reais e duplos são um pouco mais complicados, pois o tipo depende da precisão de que você precisa. No entanto, as strings devem ser capazes de ocupar menos memória em muitos casos (em .Net), onde ascii é usado principalmente, mas as strings buth sempre ocupam o dobro da memória devido à codificação unicode.
Um argumento para tipos específicos pode ser que isso faz parte da especificação, ou seja, por exemplo, uma variável não deve ser maior que um determinado valor, portanto, definimos isso como abreviação. Mas por que não ter restrições de tipo? Seria muito mais flexível e poderoso poder definir intervalos e valores permitidos em variáveis (e propriedades).
Percebo o imenso problema em renovar a arquitetura de tipos, uma vez que é tão fortemente integrada ao hardware subjacente e coisas como serialização podem se tornar realmente complicadas. Mas, do ponto de vista da programação, deve ser ótimo, não?
fonte
type hour is range 0 .. 23;
Respostas:
Eu acredito totalmente que esse seja o caso. As restrições semânticas valem mais do que as restrições de implementação. Preocupar-se com o tamanho de algo é como se preocupar com a velocidade de algo quando a programação orientada a objetos estava surgindo.
Não substituiu a programação crítica de desempenho. Ele apenas tornou a programação crítica sem desempenho mais produtiva.
fonte
Tipos adaptativos significa lógica para fazer a adaptação, trabalho em tempo de execução para executar essa lógica (o modelo e o tempo de compilação exigiriam um tipo específico, a inferência de tipo é um caso especial em que você obtém o melhor dos dois mundos). Esse trabalho extra pode ser bom em ambientes onde o desempenho não é crítico e o sistema mantém um tamanho razoável. Em outros ambientes, não é o caso (os sistemas embarcados são um, onde você precisa usar tipos inteiros de 32 / 64bits para desempenho da CPU e tipos inteiros de 8 / 16bits para otimização de backup de memória estática).
Até mesmo linguagens de uso geral que oferecem suporte à ligação tardia (resolução de tipos em tempo de execução, como o VB6) tendem a promover a digitação forte agora (VB.NET), devido ao impacto no desempenho que costumava surgir quando a ligação tardia era abusada e porque muitas vezes termine com um código feio quando os tipos não forem explícitos ( Referência / Refatoração profissional no Visual Basic - Danijel Arsenovski ).
fonte
Simplicidade, memória e velocidade Quando declaro uma variável, a memória dessa variável é alocada em um bloco. Para dar suporte a uma variável que cresce dinamicamente, eu precisaria adicionar o conceito de memória não contígua a essa variável (isso ou reservar o maior bloco que a variável pode representar). A memória não contígua reduziria o desempenho na atribuição / recuperação. Alocar o maior possível seria um desperdício no cenário em que eu só preciso de um byte, mas o sistema reserva um longo.
Pense nas vantagens e desvantagens entre uma matriz e um vetor (ou lista vinculada). Com uma matriz, procurar uma posição específica é uma simples questão de obter a posição inicial e mudar o ponteiro da memória x espaços para localizar essa nova posição na memória. Pense em um int como um pouco [32], lendo um int envolve percorrer esse array para obter todos os valores de bits.
Para criar um tipo de número dinâmico, você deve alterá-lo de uma matriz de bits para um vetor de bits. Ler seu número dinâmico envolve ir à tona, obter esse bit, perguntar onde o próximo bit está na memória, mudar para esse local, obter esse bit etc. Para cada bit no número dinâmico, você está executando três operações lidas ( atual), leia (endereço do próximo), mova (próximo). Imagine ler os valores de um milhão de números. Isso é um milhão de operações extras. Pode parecer insignificante. Mas pense nos sistemas (como finanças) em que cada milissegundo é importante.
Foi decidido que colocar o ônus no desenvolvedor para verificar o tamanho e validar é uma pequena desvantagem em comparação com o desempenho do sistema.
fonte
Tipos específicos são necessários para projetos e idiomas centrados em hardware. Um exemplo são os protocolos de rede on-the-wire.
Mas vamos criar - por diversão - um tipo de varint em uma linguagem como C ++. Construa-o a partir de uma
new
matriz de ints.Não é difícil implementar a adição: apenas x ou os bytes juntos e verifique os bits altos: se houver uma operação de transporte,
new
em um novo byte superior e repasse o bit. A subtração segue trivialmente na representação do complemento de 2. (Isso também é conhecido como adicionador de transporte de ondulação).A multiplicação segue da mesma forma; use adição / deslocamento iterativo. Como sempre, a verdadeira reviravolta na sua cauda é a divisão [*].
O que você perdeu quando isso acontece?
Tempo determinístico. Você tem um syscall (
new
) que pode ser acionado em pontos que não são necessariamente controláveis.Espaço determinístico.
A matemática do semi-software é lenta.
Se você precisa usar uma linguagem da camada de hardware e também precisa operar em um nível alto (lento) e não deseja incorporar um mecanismo de script, isso
varint
faz muito sentido. Provavelmente está escrito em algum lugar.[*] Cf algoritmos matemáticos de hardware para maneiras mais rápidas de fazer isso - geralmente o truque são operações paralelas.
fonte
Essa é uma boa pergunta. Explica por que uma linguagem como Python não precisa de "short, int, long, bigint etc.": números inteiros são, assim, números inteiros (existe um único tipo inteiro no Python 3) e não têm tamanho limite (além do tamanho de a memória do computador, é claro).
Quanto ao Unicode, a codificação UTF-8 (que faz parte do Unicode) usa apenas um único caractere para caracteres ASCII, portanto não é tão ruim assim.
De um modo mais geral, as linguagens dinâmicas parecem seguir a direção mencionada. No entanto, por razões de eficiência, tipos mais restritos são úteis em alguns casos (como programas que precisam ser executados rapidamente). Não vejo muitas mudanças no futuro próximo, pois os processadores organizam os dados em bytes (ou 2, 4, 8, etc. bytes).
fonte
Com base na teoria da linguagem, você está certo. Os tipos devem se basear em um conjunto de estados legais, nas transformações disponíveis para esses estados e nas operações executáveis nesses estados.
Isso é aproximadamente o que a programação OOP em sua forma típica fornece, no entanto. De fato, em Java, você está efetivamente falando sobre as classes
BigInteger
eBigDecimal
, que alocam espaço com base em quanto é necessário para armazenar o objeto. (Como observou o FrustratedWithFormsDesigner, muitas linguagens do tipo script estão ainda mais nesse caminho e nem exigem uma declaração do tipo e armazenam o que você fornecer.)No entanto, o desempenho ainda é relevante e, como é caro alternar tipos em tempo de execução e como os compiladores não podem garantir o tamanho máximo de uma variável no tempo de compilação, ainda temos variáveis de tamanho estatístico para tipos simples em muitos idiomas.
fonte
int
ou adouble
, e se não, eles estão cientes disso, então o dimensionamento dinâmico de valor é um recurso pelo qual eles não precisam pagar.Depende do idioma. Para linguagens de nível superior, como Python, Ruby, Erlang e outras, você só tem o conceito de números integrais e decimais.
No entanto, para uma determinada classe de idiomas com esses tipos, é muito importante. Quando você está escrevendo um código para ler e gravar formatos binários como PNG, JPeg, etc., precisa saber exatamente quantas informações estão sendo lidas por vez. O mesmo acontece com a gravação de kernels do sistema operacional e drivers de dispositivo. Nem todo mundo faz isso e, nas linguagens de nível superior, usam as bibliotecas C para realizar o trabalho pesado detalhado.
Em
short
, ainda há um lugar para os tipos mais específicos, mas muitos problemas de desenvolvimento não exigem essa precisão.fonte
Recentemente, criei um editor de lógica ladder e o tempo de execução e decidi ser muito limitado com os tipos:
Eu acredito que tornou mais intuitivo para o usuário. Esta é uma mudança radical da maioria dos PLCs, com todos os tipos "normais" de tipos que você veria em um idioma como C.
fonte
As linguagens de programação estão se movendo nessa direção. Pegue as cordas, por exemplo. Em idiomas antigos, é necessário declarar o tamanho da string, como
PIC X(42)
em COBOL,DIM A$(42)
em algumas versões do BASIC ou [VAR
]CHAR(42)
no SQL. Nas línguas modernas, você apenas tem umstring
tipo alocado dinamicamente e não precisa pensar no tamanho.Os números inteiros são diferentes, no entanto:
Dê uma olhada no Python. Ele costumava distinguir entre números inteiros de tamanho de máquina (
int
) e de tamanho arbitrário (long
). No 3.x, o antigo se foi (o antigolong
é o novoint
) e ninguém sente falta.Mas ainda existe um tipo especializado para sequências de números inteiros de 8 bits na forma de
bytes
ebytearray
. Por que não usar umtuple
oulist
de números inteiros, respectivamente? É verdadebytes
que existem métodos extras semelhantes a stringstuple
, mas certamente a eficiência tem muito a ver com isso.Na verdade não. A abordagem "tudo é de precisão dupla" é muito comum.
fonte
unum64 += ring32a-ring32b
sempre traga o comportamento correto, independentemente de o tipo inteiro padrão ser 16 bits ou 64 [observe que o uso de+=
é essencial; uma expressão comounum64a = unum64b + (ring32a-ring32b);
deve ser rejeitada como ambígua.]Eu entendo o raciocínio, variáveis / objetos são mantidos na memória, a memória precisa ser alocada e, portanto, precisamos saber quão grande pode ser uma variável. Mas, realmente, uma linguagem de programação moderna não deveria ser capaz de lidar com "tipos adaptativos", isto é, se algo é alocado apenas no intervalo curto, ele usa menos bytes e se algo é alocado repentinamente em um número muito grande, a memória é alocada de acordo com essa instância em particular.
Flutuação, reais e duplos são um pouco mais complicados, pois o tipo depende da precisão de que você precisa. No entanto, as strings devem ser capazes de ocupar menos memória em muitos casos (em .Net), onde ascii é usado principalmente, mas as strings buth sempre ocupam o dobro da memória devido à codificação unicode.
Fortran teve algo semelhante (não sei se é exatamente isso que você quer dizer, já que estou vendo duas perguntas realmente). Por exemplo, no F90 para cima, você não precisa definir explicitamente o tamanho do tipo , por assim dizer. O que é bom, não apenas porque fornece um local central para definir seus tipos de dados, mas também uma maneira portátil de defini-los. REAL * 4 não é o mesmo em todas as implementações em todos os processadores (e por processador quero dizer CPU + compilador), não por um longo tiro.
selected_real_kind (p, r) retorna o valor de tipo de um tipo de dados real com precisão decimal maior que pelo menos p dígitos e intervalo de expoentes maior que pelo menos r.
Então você vai, por exemplo;
(Eu acho que é um exemplo bastante auto-explicativo).
Ainda não sei se entendi sua pergunta corretamente, e é isso que você mencionou.
fonte