Object Pascal

Tipi di dato                   

Il Pascal, in tutte le sue incarnazioni, ha 3 tipi di dato aggregabili in 3 gruppi:
  • ordinali
  • reali
  • stringhe
Esiste poi, in Free Pascal e Object Pascal, il tipo Variant, un po' particolare in verità, come vedremo, e qualcos'altro ancora di cui, per il momento, possiamo fare a meno.
Iniziamo a parlare dei tipi ordinali, che sono i più numerosi in questo linguaggio.
Come leggerete su numerosi testi e documenti, i tipi ordinali si basano sul concetto di sequenza per cui è possibile richiedere confronto e ricavare il precedente ed il successivo di ciascun elemento che state esaminando. Essi hanno un massimo ed un minimo e verrà originato un errore cercando di andare oltre quei limiti. Ad ogni modo per essi sono disponibili ed utilizzabili alcune funzioni che sono di valenza generalizzata per ogni ordinale, li vediamo nella tabella 3.1:

Tabella 3.1  
Funzione Descrizione
Dec Decrementa l'argomento di una unità (default) o di quante indicate nel secondo, opzionale, parametro
Inc Incrementa l'argomento di una unità (default) o di quante indicate nel secondo, opzionale, parametro
Odd Restituisce True se l'argomento è pari, false altrimenti
Pred Restituisce il precedessore dell'argomento, in ossequio  alla tipologia
Succ Restituisce il successore dell'argomento, in ossequio  alla tipologia
Ord Restituisce un numero che indica la posizione dell'argomento nel suo insieme (usato per ordinali non numerici)
Low Restituisce il valore più basso del range a cui appartiene il parametro
High Restituisce il valore più alto del range a cui appartiene il parametro

Il comportamento delle ultime due è un po' particolare e diverso da quanto si riscontra in altri linguaggi. In pratica, per conoscere il massimo valore ammesso per un tipo ordinale possiamo scrivere, ad esempio riferendoci agli interi:

Writeln(High(3)); oppure
Writeln(High(7)); ecc...

il risultato sarà sempre lo stesso (nei due casi presentati l'output sarà il numero 127), il che non è proprio "bello", a mio avviso.

Il nostro percorso inizia, come spesso avviene, con i numeri interi,essi sono disponibili in vari "formati" , vediamo la tabella seguente:

Tabella 3.2    
Nome Ampiezza Range
ShortInt 8 bit -128 +127
Byte (senza segno) 8 bit 0 +255
SmallInt 16 bit -32768 + 32767
Word (senza segno) 16 bit 0 +65535
Integer 32 bit -2147483648 +2147483647
Cardinal (senza segno) 32 bit 0 +4294967295
Int64 64 bit  -9223372036854775808 +9223372036854775807
Uint64 (senza segno) 64 bit 0 +18446744073709551615

In realtà sono presenti degli alias per poter ricordare più facilmente questi tipi, in particolare ne specificano meglio la natura:

Int8   = ShortInt;
Int16  = SmallInt;
Int32  = Integer;
UInt8  = Byte;
UInt16 = Word;
UInt32 = Cardinal;

Le operazioni di base, sono le solite:

  Esempio 3.1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
program Project1;

var x1: Integer = 3;
x2: Integer = 7;

begin
  Writeln(x1 + x2);
  Writeln(x2 - x1);
  Writeln(x1 * x2);
  Writeln(x2 / x1);
  Writeln(x2 div x1);
  Writeln(x2 mod x1);
  Readln();
end.

Non c'è nulla di particolare, direi, la cosa che però dovrebbe saltare un po' all'occhio è la possibilità di eseguire la divisione in due modi diversi. Attraverso div otteniamo la parte intera del risultato, mentre se usiamo / otteniamo il risultato "vero". L'operatore mod invece ci restituisce il resto della divisione.
per quanto riguarda gli interi ci sono anche parecchie altre operazioni possibili che semplificano l'utilizzo abituale:

Tabella 3.3  
Funzione Descrizione
StrToInt(s) Converte da stringa a intero
IntToStr(i) Converte da intero a stringa
HexStr(valore, spazi) Converte a stringa usando la rappresentazione esadecimale, deve essere specificato anche il numero di caratteri della stringa
BinStr(valore, spazi) Converte in formato binario, anche qui bisogna sepcificare la dimensione della stringa di output
OctStr(valore, spazi) Converte in formato ottale, è necessario specificare, anche in questo caso, le dimensioni della stringa di output
ToExtended Converte a Extended

Ci vuole, a questo punto, un bell'esempio:

  Esempio 3.2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
program Project1;
uses sysutils;
var x: Integer;
s : string;
begin
  Write('Inserisci un numero: ');
  Readln(s);
  x := StrToInt(s);
  Writeln(x + 2);
  Writeln(HexStr(x,9));
  Writeln(BinStr(x,32));
  Writeln(OctStr(x, 16));
  Readln();
end.

Naturalmente quando riversate un intero in un una stringa bisogna che prevediate una dimesnione sufficiente di quest'ultima per ospitare il risultato, in particolar modo passando alla rappresentazione binaria. Il passaggio inverso, ad esempio da esadecimale a intero, anche in questo caso stando attenti a dove riversate il valore, è un po' più complicato, ve lo presento per darvi un'idea di come fare ma ci sarà tempo per approfondire:

  Esempio 3.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
program Project1;
uses sysutils;
var x, y: Integer;
s, s1, s2 : string;
begin
Write('Inserisci un numero: ');
  Readln(s);
  x := StrToInt(s);
  s1 := HexStr(x, 10);
  s2 := '$' + s1;
  y := StrToInt(s2);
  Writeln(s1);
  Writeln(y);
  Readln();
end.

Noterete la riga 10 che effettua un migrazione della stringa in formato standard esadecimale. Alla 2, anche nell'esempio 3.2, trovate la keyword uses che vi permette di utilizzare la libreria sysutils, che, come dice il nome, contiene una serie di funzioni di uso comune.

Quando ci sono così tanti tipi diversi che esprimono una stessa tipologia, i numeri interi, nello specifico, viene sempre da chiedersi cosa accade quando si esce dal range predefinito. E' una questione che attanaglia tanti linguaggi, creando grattacapi ai neofiti. In alcuni casi, ricordo ad esempio il linguaggio Fantom, la cosa è stata risolta proponendo solo ed esclusivamente interi a 64 bit, soluzione semplice e funzionale che però "costa", userete infatti parecchia memoria anche per rappresentare un semplice "1".

  Esempio 3.4
1
2
3
4
5
6
7
8
program Project1;
var
x: byte = 100;
begin
  x := x + 200;
  Writeln(x);
  Readln();
end.

Il risultato di questo programma è 44 invece del 300 che sarebbe atteso. Il perchè è abbastanza semplice se vi rifate alla rappresentazione in bit dei numeri. Detto rapidamente, 300 in binario è
‭000100101100‬ mentre 44 è  ‭00101100‬ il resto, sapendo che un dato di tipo byte si sviluppa su 8 bit, lo lascio a voi. Così come lascio a voi capire perchè  ad esempio 100 + 50 applicato ad uno ShortInt restituisce -106. Il discorso è sempre lo stesso. Il problema, caso mai, è il fatto che tutto avvenga in modo silente. Ci sono le tecniche per controllare queste cose ma sono un po' più avanzate di quanto in argomento adesso. Per il momento tenete presente questa criticità. Ad ogni modo, se volete una via semplice, potete istruire il vostro compilatore a tenere sotto controllo i casi di overflow; consultate la documentazione del vostro strumento di sviluppo, sia esso Delphi, Lazarus ecc....

Concludiamo il paragrafo con un argomento collegato ai numeri, non necessariamente solo agli interi, di estrema utilità, ovvero la libreria Math. Questa è ricca di funzioni precotte che vi saranno utili quando si tratterà di lavorare in ambito matematico. troverete funzioni trigonometriche, logaritmiche, l'elevamento a potenza, massimi, minimi , insomma parecchia roba utile, per farla breve. Questo almeno è il corredo dei compilatori Pascal più diffusi. Vediamo un breve esempio che permette di rivedere in azione anche la la keyword uses che avete visto nello scorso capitolo e negli esempi 3.2 e 3.3 .

  Esempio 3.5
1
2
3
4
5
6
7
8
9
10
program Project1;
uses Math;
var
x: Integer = 7;
y: Integer = 5;
begin
  Writeln(power(x, y));
  Writeln(max(x,y));
  Readln();
end.