Sono ancora necessari tipi specifici?


20

Una cosa che mi è venuta in mente l'altro giorno, sono tipi specifici ancora necessari o un retaggio che ci sta trattenendo. Quello che voglio dire è: abbiamo davvero bisogno di short, int, long, bigint ecc ecc.

Capisco il ragionamento, le variabili / gli oggetti sono tenuti in memoria, la memoria deve essere allocata e quindi dobbiamo sapere quanto può essere grande una variabile. Ma in realtà, un moderno linguaggio di programmazione non dovrebbe essere in grado di gestire "tipi adattivi", cioè se qualcosa viene allocato sempre e solo nell'intervallo di shortint usa meno byte, e se qualcosa viene improvvisamente assegnato un numero molto grande la memoria viene allocata per quel particolare caso.

Float, real e double sono un po 'più complicati poiché il tipo dipende dalla precisione di cui hai bisogno. Le stringhe dovrebbero tuttavia essere in grado di occupare meno memoria in molti casi (in .Net) in cui viene utilizzato principalmente ASCII, ma le stringhe occupano sempre il doppio della memoria a causa della codifica Unicode.

Un argomento per tipi specifici potrebbe essere che fa parte della specifica, ad esempio una variabile non dovrebbe essere in grado di essere più grande di un certo valore, quindi la impostiamo su shortint. Ma perché non avere vincoli di tipo invece? Sarebbe molto più flessibile e potente essere in grado di impostare intervalli e valori consentiti su variabili (e proprietà).

Mi rendo conto dell'immenso problema nel rinnovare l'architettura dei tipi poiché è così strettamente integrato con l'hardware sottostante e cose come la serializzazione potrebbero diventare davvero difficili. Ma dal punto di vista della programmazione dovrebbe essere fantastico no?


6
PHP, Ruby, Perl e altri non richiedono di indicare i tipi di variabili. L'ambiente lo capisce per te.
FrustratedWithFormsDesigner,

7
Le stringhe Unicode non devono occupare memoria aggiuntiva quando vengono utilizzate solo per ASCII (UTF-8).

2
Ma c'è una differenza tra variante e tipi adattivi IMO. Le varianti non vengono affatto digitate ma vengono digitate quando assegnate, mentre i tipi adattivi verrebbero digitati, ma più liberamente. (e mi piace il concetto di vincoli di tipo)
Homde,

Questo mi ricorda questo progetto: tom.lokhorst.eu/media/…
LennyProgrammers

4
Che dire di Ada? type hour is range 0 .. 23;
mouviciel,

Risposte:


12

Credo assolutamente che sia così. I vincoli semantici valgono più dei vincoli di implementazione. Preoccuparsi delle dimensioni di qualcosa sembra preoccuparsi della velocità di qualcosa quando stava avvenendo la programmazione orientata agli oggetti.

Non ha sostituito la programmazione critica per le prestazioni. Ha semplicemente reso più produttiva la programmazione non critica delle prestazioni.


1
Scopri i contratti di codice in .NET 4.0.
Steven Jeuris,

+1 Quando si tratta di archiviazione / trasmissione dei dati (es. Networking) i vincoli sono fondamentali per massimizzare l'efficienza del protocollo / implementazione. Inoltre, c'è molto terreno da guadagnare se sono disponibili raccolte tipizzate. A parte questo, è lecito ritenere che l'efficienza possa fare un passo indietro (specialmente se diminuisce la possibilità di errori semantici).
Evan Plaice,

9

Tipi adattativi significa logica per fare l'adattamento, significa lavoro in fase di esecuzione per eseguire quella logica (templating e tempo di compilazione richiederebbero un tipo specifico, l' inferenza del tipo è un caso speciale in cui si ottiene il meglio dei due mondi). Quel lavoro extra potrebbe andare bene in ambienti in cui le prestazioni non sono critiche e il sistema mantiene dimensioni ragionevoli. In altri ambienti non lo è (i sistemi integrati sono uno, dove a volte è necessario utilizzare tipi interi a 32/64 bit per le prestazioni della cpu e tipi interi a 8/16 bit per l'ottimizzazione del backup della memoria statica).

Anche i linguaggi di uso generale che supportano l' associazione tardiva (risoluzione dei tipi in fase di runtime, come VB6) tendono a promuovere la tipizzazione avanzata ora (VB.NET), a causa del colpo di prestazioni che si presentava quando veniva abusata dell'associazione tardiva e perché spesso finisce con un brutto codice quando i tipi non sono espliciti ( Riferimenti / Refactoring professionale in Visual Basic - Danijel Arsenovski ).


Si prega di definire "la digitazione automatica".

@delnan: sostituita la digitazione automatica con l'associazione tardiva che intendevo dire :)
Matthieu,

Esistono molti linguaggi generici che risolvono i tipi in fase di esecuzione, Common Lisp per nominarne solo uno. (Ai fini delle prestazioni, puoi dichiarare i tipi in Common Lisp, quindi puoi farlo solo nelle sezioni critiche per le prestazioni.)
David Thornley,

@David Thornley: "imporre" la digitazione forte potrebbe essere stato troppo forte, "promuovere" sarebbe più appropriato, aggiornato la mia risposta di conseguenza. Una lingua che ti consente di scegliere tra i due tipi di rilegatura a seconda della situazione è sicuramente meglio che essere costretti in un modo o nell'altro. Soprattutto quando non si esegue la programmazione di basso livello e si concentra sulla logica.
Matthieu,

4

Semplicità, memoria e velocità Quando dichiaro una variabile, la memoria per quella variabile viene allocata in un blocco. Per supportare una variabile a crescita dinamica, dovrei aggiungere il concetto di memoria non contigua a quella variabile (o quello o riservare il blocco più grande che la variabile può rappresentare). La memoria non contigua ridurrebbe le prestazioni su assegnazione / recupero. Allocare il più grande possibile sarebbe dispendioso nello scenario in cui ho solo bisogno di un byte ma il sistema riserva molto.

Pensa ai compromessi tra un array e un vettore (o un elenco collegato). Con un array, cercare una posizione specifica è una semplice questione di ottenere la posizione iniziale e spostare il puntatore della memoria x gli spazi per individuare quella nuova posizione nella memoria. Pensa a un int come a un bit [32] la lettura di un int comporta il passaggio attraverso quell'array per ottenere tutti i valori di bit.

Per creare un tipo di numero dinamico, è necessario modificarlo da una matrice di bit a un vettore di bit. Leggere il tuo numero dinamico implica andare in testa, ottenere quel bit, chiedere dove si trova il bit successivo in memoria, spostarsi in quella posizione, ottenere quel bit, ecc. Per ogni bit nel numero dinamico, stai facendo tre operazioni di lettura ( corrente), leggi (indirizzo del prossimo), sposta (successivo). Immagina di leggere i valori di un milione di numeri. Sono un milione di operazioni extra. Potrebbe sembrare insignificante. Ma pensa ai sistemi (come quelli finanziari) in cui ogni millisecondo conta.

È stata presa la decisione che affidare allo sviluppatore il compito di controllare le dimensioni e convalidare sia un piccolo compromesso rispetto a influenzare le prestazioni del sistema.


1
L'altra alternativa è quella di implementare numeri simili agli array, in cui l'array viene riassegnato quando il numero supera la dimensione corrente. Inoltre, è necessario tenere conto del caso in cui l'utente VUOLE l'overflow per eseguire il loop.
Michael Brown,

È vero, ma in qualche modo una semplificazione. Potresti trovare una struttura di array più efficiente, mentre non tanto veloce quanto tipizzato staticamente potrebbe essere "abbastanza veloce" per la maggior parte dei casi. ad esempio potresti salvare informazioni su blocchi di diversi tipi, se l'array non fosse completamente frastagliato che non occuperebbe molta più memoria o prestazioni. Oppure l'array potrebbe sacrificare un po 'di memoria per avere un indice di qualche tipo. L'array potrebbe persino auto-ottimizzarsi in base al suo contenuto. Potresti comunque avere la possibilità di digitare il memorysize attraverso un vincolo di tipo se hai bisogno di prestazioni.
Homde,

Ad essere onesti, non è così brutale come si fa a capire. Cf la mia prossima risposta.
Paul Nathan,

3

Tipi specifici sono richiesti per linguaggi e progetti incentrati sull'hardware. Un esempio sono i protocolli di rete on-the-wire.

Ma creiamo, per divertimento, un tipo di variante in un linguaggio come C ++. Costruiscilo da una newvasta gamma di ints.

Non è difficile implementare l'aggiunta: basta solo xor i byte insieme e controllare i bit alti: se c'è un operazione di carry, newin un nuovo byte superiore e riporto il bit. La sottrazione segue banalmente nella rappresentazione del complemento di 2. (Questo è anche noto come un sommatore trasportatore di ripple).

La moltiplicazione segue allo stesso modo; usa l'aggiunta / spostamento iterativo. Come sempre, la vera svolta nella tua coda è la divisione [*].

Cosa hai perso quando questo accade, però?

  • Tempo deterministico. Hai un syscall ( new) che può innescarsi in punti che non sono necessariamente controllabili.

  • Spazio deterministico.

  • La matematica del semi-software è lenta.

Se hai bisogno di usare un linguaggio a livello hardware e devi anche operare a un livello alto (lento) e non vuoi incorporare un motore di script, varintha molto senso. Probabilmente è scritto da qualche parte.

[*] Algoritmi matematici hardware Cf per modi più veloci di farlo - di solito il trucco sono operazioni parallele.


2

Questa è una buona domanda Spiega perché un linguaggio come Python non ha bisogno di "short, int, long, bigint ecc.": I numeri interi sono, beh, numeri interi (esiste un singolo tipo intero in Python 3) e non hanno dimensioni limite (oltre quella di la memoria del computer, ovviamente).

Per quanto riguarda Unicode, la codifica UTF-8 (che fa parte di Unicode) utilizza solo un singolo carattere per i caratteri ASCII, quindi non è poi così male.

Più in generale, i linguaggi dinamici sembrano andare nella direzione menzionata. Tuttavia, per motivi di efficienza, in alcuni casi sono utili tipi più limitati (come i programmi che devono essere eseguiti rapidamente). Non vedo molti cambiamenti nel prossimo futuro, poiché i processori organizzano i dati in byte (o 2, 4, 8, ecc. Byte).


1

Su una base di teoria del linguaggio hai ragione. I tipi dovrebbero essere basati su una serie di stati legali, le trasformazioni disponibili per tali stati e le operazioni eseguibili su tali stati.

Questo è all'incirca ciò che ti dà la programmazione OOP nella sua forma tipica. In effetti, in Java, stai effettivamente parlando delle classi BigIntegere BigDecimal, che allocano lo spazio in base a quanto è necessario per memorizzare l'oggetto. (Come ha notato FrustratedWithFormsDesigner, molti linguaggi di tipo scripting sono ancora più avanti lungo questo percorso e non richiedono nemmeno una dichiarazione di tipo e memorizzeranno tutto ciò che gli dai.)

Le prestazioni sono comunque rilevanti, e poiché è costoso cambiare tipo in fase di esecuzione e poiché i compilatori non possono garantire la dimensione massima di una variabile in fase di compilazione, abbiamo ancora variabili di dimensioni statiche per tipi semplici in molte lingue.


Mi rendo conto che una sorta di tipizzazione dinamica / adattiva sembra costosa e meno performante di quella che abbiamo ora, e usando i compilatori attuali lo sarebbero sicuramente. Ma siamo sicuri al 100% che se costruisci un linguaggio e un compilatore da zero non potresti farli, se non così velocemente come digitati staticamente, almeno fattibile per valerne la pena.
Homde,

1
@MKO: Perché non lo provi e vedi?
Anon.

1
Sì, puoi renderlo fattibile velocemente (ma probabilmente mai così velocemente come un sistema statico per i numeri). Ma la parte "ne vale la pena" è più complicata. La maggior parte delle persone lavora con dati il ​​cui intervallo si adatta comodamente a una into a double, e in caso contrario, ne sono consapevoli, quindi il dimensionamento dinamico del valore è una caratteristica per la quale non è necessario pagare.
giovedì

Come tutti i programmatori ovviamente sogno un giorno di creare la mia lingua;)
Homde,

@jprete: non sono d'accordo; la maggior parte delle persone non è a conoscenza di possibili risultati intermedi di grandi dimensioni. Tale linguaggio può ed è stato reso abbastanza veloce per la maggior parte degli scopi.
David Thornley,

1

Dipende dalla lingua. Per linguaggi di livello superiore come Python, Ruby, Erlang e simili hai solo il concetto di numeri interi e decimali.

Tuttavia, per una certa classe di lingue questi tipi sono molto importanti. Quando si scrive codice per leggere e scrivere formati binari come PNG, JPeg, ecc., È necessario sapere con precisione quante informazioni vengono lette alla volta. Lo stesso vale per la scrittura di kernel del sistema operativo e driver di dispositivo. Non tutti lo fanno, e nei linguaggi di livello superiore usano le librerie C per eseguire il lavoro pesante dettagliato.

In short, c'è ancora posto per i tipi più specifici, ma molti problemi di sviluppo non richiedono quella precisione.


0

Di recente ho creato un editor di logica ladder e un runtime e ho deciso di essere molto limitato con i tipi:

  • booleano
  • Numero
  • Corda
  • Appuntamento

Credo che lo abbia reso più intuitivo per l'utente. Questo è un allontanamento radicale dalla maggior parte dei PLC che hanno tutta la gamma "normale" di tipi che vedresti in una lingua come C.


0

I linguaggi di programmazione si sono mossi in quella direzione. Prendi le stringhe per esempio. Nei vecchi linguaggi devi dichiarare la dimensione della stringa, come PIC X(42)in COBOL, DIM A$(42)in alcune versioni di BASIC o [ VAR] CHAR(42)in SQL. Nelle lingue moderne hai solo un stringtipo allocato dinamicamente e non devi pensare alle dimensioni.

I numeri interi sono diversi, tuttavia:

Quello che voglio dire è: abbiamo davvero bisogno di short, int, long, bigint ecc ecc.

Dai un'occhiata a Python. Ha usato per distinguere tra interi di dimensioni macchina ( int) e di dimensioni arbitrarie ( long). In 3.x il primo è sparito (il vecchio longè il nuovo int) e nessuno lo manca.

Ma esiste ancora un tipo specializzato per le sequenze di numeri interi a 8 bit sotto forma di bytese bytearray. Perché non usare a tupleo listdi numeri interi, rispettivamente? È vero, bytesha metodi extra simili a stringhe che tuplenon lo fanno, ma sicuramente l'efficienza ha avuto molto a che fare con esso.

Float, real e double sono un po 'più complicati poiché il tipo dipende dalla precisione di cui hai bisogno.

Non proprio. L'approccio "tutto è doppia precisione" è molto comune.


1
Forse i tipi di base dovrebbero dichiarare l'intento di base del tipo, ovvero int per i numeri "ordinari", doppio per tutti i "decimali" normali (gli ints non dovrebbero essere in grado di avere decimali per semplicità?) "Soldi" per lavorare con importi e byte per lavorare con dati binari. Un vincolo di tipo dichiarato tramite un attributo potrebbe consentire di dichiarare intervallo consentito, precisione decimale, nullability e persino valori consentiti. Sarebbe bello se tu potessi creare tipi personalizzati e riutilizzabili in quel modo
Homde

@konrad: IMHO, la ragione per cui numeri interi "senza segno" causano tali mal di testa in C è che a volte vengono usati per rappresentare numeri e talvolta per rappresentare membri di un anello algebrico astratto avvolgente. Avere tipi "ring" e "numero senza segno" separati potrebbe garantire che il codice simile unum64 += ring32a-ring32bfornisca sempre il comportamento corretto, indipendentemente dal fatto che il tipo intero predefinito sia 16 bit o 64 [si noti che l'uso di +=è essenziale; un'espressione simile unum64a = unum64b + (ring32a-ring32b);dovrebbe essere respinta come ambigua.]
supercat

0

Capisco il ragionamento, le variabili / gli oggetti sono tenuti in memoria, la memoria deve essere allocata e quindi dobbiamo sapere quanto può essere grande una variabile. Ma in realtà, un moderno linguaggio di programmazione non dovrebbe essere in grado di gestire "tipi adattivi", cioè se qualcosa viene allocato sempre e solo nell'intervallo di shortint usa meno byte, e se qualcosa viene improvvisamente assegnato un numero molto grande la memoria viene allocata per quel particolare caso.

Float, real e double sono un po 'più complicati poiché il tipo dipende dalla precisione di cui hai bisogno. Le stringhe dovrebbero tuttavia essere in grado di occupare meno memoria in molti casi (in .Net) in cui viene utilizzato principalmente ASCII, ma le stringhe occupano sempre il doppio della memoria a causa della codifica Unicode.

Fortran ha avuto qualcosa di simile (non so se questo è esattamente ciò che intendi, dal momento che vedo davvero due domande). Ad esempio, in F90 verso l'alto non è necessario definire esplicitamente una dimensione del tipo , per così dire. Il che è positivo, non solo perché ti dà un posto centrale per definire i tuoi tipi di dati, ma anche un modo portatile per definirli. REAL * 4 non è lo stesso in tutte le implementazioni su tutti i processori (e per processore intendo CPU + compilatore), non per un lungo periodo.

selected_real_kind (p, r) restituisce il valore gentile di un tipo di dati reale con precisione decimale maggiore di almeno p cifre e intervallo esponente maggiore almeno r.

Quindi vai, per esempio;

program real_kinds
integer,parameter :: p6 = selected_real_kind(6)
integer,parameter :: p10r100 = selected_real_kind(10,100) !p is precision, r is range
integer,parameter :: r400 = selected_real_kind(r=400)
real(kind=p6) :: x
real(kind=p10r100) :: y
real(kind=r400) :: z

print *, precision(x), range(x)
print *, precision(y), range(y)
print *, precision(z), range(z)
end program real_kinds

(Penso che sia un esempio piuttosto autoesplicativo).

Ancora non so se ho capito bene la tua domanda, e questo è ciò che dici.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.