Seed7

Tipi di dato e basi

Consueto inizio che tratta i tipi di dato disponibili. Dobbiamo pur sapere cosa possiamo usare o no?

Il tipo più semplice è costituito dai booleani. Essi sono caratterizzati dal fatto di poter assumere i soliti due valori: TRUE e FALSE, maiuscoli.

  Esempio 2-1
1
2
3
4
5
6
7
8
9
10
$ include "seed7_05.s7i";

const proc: main is func
local
var boolean: b1 is TRUE;
begin
  writeln(b1);
  b1 := not b1;
  writeln(b1);
end func;

Qualcosa di nuovo lo vediamo subito: la riga 5 ci presenta una dichiarazione di variabile. Il formato è semplice:

var tipo: nomevariabile is valore iniziale

come abbiamo detto nel capitolo introduttivo ogni variabile o costante deve essere inizializzata. Non c'è inizializzazione di default. L'interprete si lamenta in modo persuasivo in caso manchi il valore iniziale.
La riga 4 ci presenta una nuova istruzione: local. Evidentemente questo limita alla sola procedura corrente il l'uso delle variabili nella sezione inizializzata tramite local.

Tornando ai nostri booleani anch'essi hanno qualche particolarità, nella loro semplicità che vediamo nel seguente esempio che invito a guardare con attenzione:

  Esempio 2-2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ include "seed7_05.s7i";

const proc: main is func
local
var boolean: b1 is TRUE;
begin
writeln(b1);
b1 := not b1;
writeln(b1);
writeln(ord(b1));
writeln(ord(not b1));
writeln(succ(b1));
(* writeln(succ(TRUE)); questo darebbe errore se eseguito*)
(* writeln(pred(FALSE)); anche questo darebe errore *)
writeln(str(FALSE));
end func;

Passiamo ora agli interi. Si tratta dei classici numeri interi che conosciamo e che, nel caso specifico, devono essere definiti su almeno 32 bit. Il formato con cui essi si presentano di norma è quello di una sequenza di cifre, non c'è bisogno che lo dica, ma sono ammessi altri formati che vediamo di seguito:

123456  - normale
2E6 - sta per 2 * 10^6 ovvero 2000000
2e+3 - sta per 2000
16#ABCD - formato esadecimale introdotto da 16#
8#7777 - formato ottale introdotto da 8#
2#101 - formato binario introdotto da 2#


abbastanza originale la presentazione dei formati esadecimale, ottale e binario.

Esempio 2-3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ include "seed7_05.s7i";

const proc: main is func
local
var integer: x1 is 3;
var integer: x2 is 1e+3;
var integer: x3 is 16#f;
var integer: x4 is 2#11;
var integer: x5 is 36#Z;
begin
writeln(x1);
writeln(x1 + x2);
writeln(x1 + x2 + x3);
writeln(x1 + x2 + x3 + x4);
writeln(x5);
end func;

La cosa interessante è che si può utilizzare il carattere # per generare numerazioni con base fino a 36, utilizzando tutte le lettere fino a Z, come si può evincere dall'esempio 2-3 alla riga 9 che espone il suo valore tramite la 15. Si vive anche senza questa feature ma può essere utile anche a scopo didattico.
Le operazioni di base non differiscono da quanto è possibile trovare in altri linguaggi.

  Esempio 2-4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ include "seed7_05.s7i";

const proc: main is func
local
var integer: x1 is 3;
var integer: x2 is 7;
begin
writeln(x1 + x2);     (* somma *)
writeln(x2 - x1);     (* sottrazione *)
writeln(x1 * x2);     (* moltiplicazione *)
writeln(x2 div x1);   (* divisione con quoziente intero *)
writeln(x2 rem x1);   (* resto intero di una divisione *)
writeln(x2 ** x1);    (* elevamento a potenza *)
writeln(!x2);         (* fattoriale *)
end func;

Esistono poi due operatori ulteriori per la divisione mod e mdiv che differiscono per il troncamente e di cui riparleremo. Per ora bastano questi per portare a termine i nostri semplici esempi di base.
Seed7 prevede anche un altro tipo di intero che si chiama bigInteger. Questi sono interi che non hanno limiti se non nella memoria del sistema che ospita il programma (nel caso in cui sia saturata ne esce un errore di tipo MEMORY_ERROR. Sono contenuti formalmente nella libreria bigInt.s7i e sono indicati come normali interi seguiti da simbolo _ (underscore). Ad esempio:

1_ o 8_

sono esempi (piccoli :-) di bigInteger. Vediamoli all'opera con la premesa che, ovviamente, per essi valgono le stesse regole e operatori applicabili agli interi:

  Esempio 2-5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ include "seed7_05.s7i";
include "bigint.s7i";

const proc: main is func
local
var bigInteger: x1 is 3000999999_;
var bigInteger: x2 is 0_;
var bigInteger: x3 is 0_;
begin
readln(x2);
writeln(x1);
x3 := x1 * x2;
writeln(x3);

end func;

e ora potete sbizzarrirvi con le moltiplicazioni. Noterete che quando è necessario includere più di una libreria esterna si mette comunque una sola volta il simbolo di inclusione $.

Passiamo ai tipi con virgola, i float. Si tratta di numeri in singola precisione con almeno una cifra decimale (obbligatoria) dopo il punto che separa la parte intera da quella frazionale. E' possibile anche avere la presenza di una parte esponenziale introdotta da "e" o "E". Importante ricordare che i float sono gestiti tramite la libreria float.s7i. Esempi di numeri float:

2.12345
11.2345E-10
0.001
123. questo non va bene

Esempio pratico:

  Esempio 2-6
1
2
3
4
5
6
7
8
9
10
$ include "seed7_05.s7i";
include "float.s7i";

const proc: main is func
local
var float: f1 is 2.1;
begin
writeln(f1);
writeln(f1 - 2.1);
end func;

Interessante l'output esposto grazie alla riga 9. Le operazioni principali sono equivalenti a quelle viste per gli interi fatto salvo per la divisione (perchè?). Lo vediamo con un esempio:

  Esempio 2.7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ include "seed7_05.s7i";
include "float.s7i";

const proc: main is func
local
var float: f1 is 2.1;
var float: f2 is 3.4;
begin
writeln(f1 - 2.1);
writeln(f2 - f1);
writeln(f1 * f2);
writeln(f2 / f1);
writeln(f2 ** f1);
end func;

Come si vede alla riga 12 la divisione viene posta in atto con l'operatore / invece che con l'operatore div, come era per gli interi e che qui non funziona.
Un problemino che a volte può lasciare perplessi è quello di voler aver un risultato "esatto", ovvero con le cifre decimali, anche quando lavoriamo con gli interi. Alcuni linguaggi, anche di fronte a divisioni tipo 4 / 3 danno come risultato 1,333333 e per avere la parte intera da sola è necessario l'uso di un qualche metodo. Falcon, per restare ai linguaggi della mia sezione di programmazione è uno di questi. Tanti altri invece ragiinano all'opposto e per loro 4 / 3 avente divisore e dividendo interi fa solo 1. A voi scegliere cosa sia meglio. Comunque Seed7 ragiona nel secondo modo e, tra l'altro, non è possibile dividere un intero con un float e viceversa, per cui vediamo i vari casi:

  Esempio 2.8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ include "seed7_05.s7i";
include "float.s7i";

const proc: main is func
local
var integer: x1 is 4;
var integer: x2 is 3;
var float: f1 is 4.0;
var float: f2 is 3.3;
begin
  writeln(x1 div x2);
  writeln(f1 / f2);
  writeln(flt(x1) / f2);
  writeln(x1 div trunc(f2));
end func;

Le righe 11 e la 12 sono "normali". La 13 ci presenta il modo per convertire un intero in un float, ciò che avviene grazie al metodo flt. Al contrario alla riga 14 avvviene la conversione di un float in intero tramite troncamento (il metodo trunc) quindi 3.3 diventa 3. Esiste anche il metodo ceil che effettua la stessa conversione valore più vicino superiore, quindi 3.3 diventerebbe 4.0.... ovvero resta un float, non fatevi ingannare. Se volete essere sicuri di avere un intero, oltre a trunc potete usare round che, come prevedibile, arrotonda a seconda del valore float che gli viene proposto come argomento secondo la classica regola:

round(1.5)=>2, round(-1.5)=>-2,
round(0.5)=>1, round(-0.5)=>-1,
round(0.4)=>0, round(-0.4)=> 0 


Troverete poi molti altri metodi utili e comodi.

Eccoci ora a parlare dei char. Essi rappresentano i caratteri codificati UTF-32. I caratteri UTF-8 presenti normalmente nei files sono rappresentati all'interno di una coppia di doppi apici. Come in tutti i linguaggi i caratteri stampabili ma non visualizzabili sono indicati come segue:

beep                \a
backspace           \b
escape              \e
formfeed            \f
newline(LF)         \n
ritorno carrello    \r
tab orizzontale     \t
tab verticale       \v
backslash (\)       \\
singolo apice       \'
doppio apice        \"
control-A           \A
...
control-Z           \Z


Sempre cercando di mantenere un taglio pratico affrontiamo il classico problema di passare da un carattere al suo valore nella tabella ASCII e viceversa ricavare da un numero al carattere corrispondente. Per il secondo problema è molto semplice, basta mettere il numero intero all'interno di una coppia di baskslash. Oppure si usa il comodo metodo chr. Per fare il contrario possiamo usare ord. Esempio:

  Esempio 2.9
1
2
3
4
5
6
7
$ include "seed7_05.s7i";
const proc: main is func
begin
writeln("\65\");
writeln(chr(65));
writeln(ord('A'));
end func;

Le righe 6 e 7 risolvono con semplicità il problema proposto.

Per il momento, in vista dei prossimi argomenti, ci fermiamo qui preavvisandovi che Seed7 prevede altri tipi (Complex, Rational, BigRational) che analizzeremo in un altro paragrafo e che per il momento non ci servono.