Object Pascal - Sviluppo di Applicazioni Scientifiche e Tecniche

Nota: Questo articolo è stato scritto e pubblicato più di dieci anni fa, ma ora lo pubblico di nuovo, praticamente senza nessuna modifica, visto che lo trovo oggi ancora molto attuale.

Il matematico e filosofo Leibnitz sosteneva la necessità di costruire delle macchine capaci di eseguire calcoli numerici per liberare l'uomo da tale schiavitù. Siamo nel 1600. La sua non era una proposta o una provocazione, ma culminò con la costruzione di una macchina in grado di eseguire moltiplicazione e divisione tra numeri interi apportando delle migliorie alla pascalina inventata da Pascal ma che faceva solo operazioni di addizione e sottrazione. La pascalina è considerata la prima macchina calcolatrice fatta dall'uomo. Sono passati quasi quattro secoli prima di vedere il sogno di Leibnitz realizzarsi.

La necessità di disporre di macchine in grado di risolvere problemi di calcolo numerico è stata la causa scatenante a condurre alla realizzazione del "computer" di oggi che, letteralmente, significa "calcolatore".

Ad ulteriore conferma di tutto ciò, il primo linguaggio di alto livello per la programmazione dei computer fu il Fortran. La parola Fortran è un mix delle due parole FORmula TRANslator; ossia il traduttore di formule. Siamo nel 1957, ed il suo riferimento al calcolo numerico è esplicito. Il Fortran, anche se è evoluto nel tempo, ha conservato la propria tradizione di rimanere un linguaggio dedicato allo sviluppo di software di stampo scientifico-tecnico.

Oggi, tale genere di software è diventato integrato. Cioè, oltre a risolvere problemi di puro calcolo numerico, il software serve per interfacciarsi con sistemi CAD (Compuer Aided Design), database relazionali, generatori di report e comunicare con altri sistemi in rete.

Perciò, il software complesso di oggi richiede l'utilizzo di un mix di linguaggi specializzati, oppure di un unico linguaggio che sia veramente general purpose; cioè adatto per lo sviluppo di qualsiasi genere di software.

Qui, se vogliamo rimanere con i piedi per terra, il cerchio si stringe ed a portata di mano abbiamo solo il C++ e l'Object Pascal. Altrimenti, dobbiamo rivolgerci a sua maestà Ada. Il Free Pascal rappresenta una scelta estremamente bilanciata perché troviamo in questo linguaggio ed il suo compilatore molte delle virtù del Fortran, del C++ e soprattutto di Ada ad esclusione della programmazione parallela.

Struttura di un programma scientifico-tecnico

Tradizionalmente, un programma che risolve problemi di tipo scientifico, o tecnico, è composto da tre parti comunemente chiamate "pre-processore", "processore" e "post-processore". Questa suddivisione ha le sue radici nella risoluzione manuale di tale genere di problemi che si basa sulla nota sequenza di operazioni:

  1. preparazione dei dati;
  2. elaborazione simbolica e numerica;
  3. presentazione delle soluzioni.

In passato, quando i computer non erano così potenti ed il Fortran regnava sovrano, la parte più importante era quella centrale; cioè quella relativa alla pura potenza di calcolo, mentre quelle di input ed output erano semplici file di testo.

L'input da file di testo è un'operazione semplice e comoda però presenta delle notevoli inconvenienti. La probabilità di commettere un errore nei dati ed interpretare male un risultato è molta alta. Le conseguenze di una distrazione del genere possono causare delle catastrofi. Basti pensare a calcoli strutturali sballati di un edificio residenziale con più piani o di una diga.

Ecco un semplice esempio: se il tecnico inserisce il valore "550" al posto di "5.50", il programma calcola la struttura e fornisce ugualmente un risultato. Ma una trave lunga 5.50 metri si comporta in modo completamente diverso da una di 550 metri di lunghezza!

E' da sottolineare che un tecnico debba possedere un "occhio clinico" per ogni genere di problema da risolvere per evitare ogni incongruenza con la realtà.

Oggi, i PC, con avanzato hardware e a prezzi assai contenuti, offrono una formidabile potenza di calcolo e sono in grado di far girare software per la risoluzione di complessi problemi scientifici e tecnici. La possibilità di creare e visualizzare grafica ed animazione di qualità ed in tempo reale si è rivelata di grande aiuto per il tecnico progettista. Come conseguenza, questa evoluzione tecnologica ha portato ad una totale revisione dei pre e post processori.

Il pre-processore

Nelle applicazioni scientifiche e tecniche i dati sono introdotti da tastiera, mouse, file di testo, database, programmi o device particolari (scanner, rivelatori elettronici, sonde...). L'importante per il tecnico è che i dati siano correttamente inseriti prima di mandarli in elaborazione. Per controllare tale esito è necessario disporre di un'interfaccia per l'input, semplice, evoluta, piacevole ed efficiente che permetta all'utente di controllare graficamente ogni aspetto dei dati del problema da risolvere o del fenomeno da analizzare.

Per la realizzazione di un'interfaccia del genere, il Free Pascal è particolarmente adatto perché può accedere direttamente a tutte funzioni del sistema operativo ospite e di conseguenza anche all'hardware sottostante nonché a quelle della libreria grafica OpenGL e della libreria SDL (Simple DirectMedia Layer). Quest'ultima permette di controllare da codice Free Pascal molte periferiche tipo tastiera, mouse, joystick, schede audio, hardware 3D... La libreria SDL è matura, collaudata e dispone di tutto ciò che serve per lo sviluppo di applicazioni interattive e multimediali.

Il sito ufficiale della SDL per il Free Pascal si trova all'indirizzo http://sdl4fp.sourceforge.net/, mentre quelli dedicati alla grafica in Free Pascal ed OpenGL sono veramente tanti; uno in particolare è il seguente http://www.friends-of-fpc.org/ che offre un tutorial completo sull'argomento.

Il post-processore

Il post-processore è l'applicazione dedicata alla visualizzazione dei risultati scaturiti dall'elaborazione numerica. Anche se questi risultati dipendono dalla natura del problema in questione, generalmente, un post-processore dispone di un versatile editor di testo, una griglia per i risultati espressi in formato tabellare, un'applicazione per la visualizzazione di grafica vettoriale 2D/3D ed una per quella raster.

In più, è necessaria la presenza di un modulo per la conversione dell'output grafico nei formati comunemente più usati come il DXF per la grafica vettoriale e JPEG per quella raster. Ultimamente, i post-processore sono diventati così completi e sofisticati da rappresentare una categoria di software autonoma. Basterebbe dare un'occhiata ad OpenDX (http://www.opendx.org/index2.php) per rendersi conto a che livelli sono arrivati oggi alcuni post-processori.

Scrivere un post-processore per un'applicazione specifica in Free Pascal è assai conveniente sia per la presenza di vaste librerie dedicate che possano essere utilizzate direttamente dal linguaggio, sia per la facilità con la quale si possono integrare processore e post-processore.

Il processore

Il processore è storicamente la parte più importante dell'intero pacchetto e, perciò, nel suo sviluppo è stata sempre messa molta cura per evitare ogni tipo di errore. Qui si macinano i numeri ed avviene la ricerca delle soluzioni del problema da risolvere. Un errore nel processore potrebbe compromettere l'esattezza della soluzione con le relative conseguenze.

Il processore è la parte "buia" di un pacchetto software scientifico o tecnico; cioè racchiude tutti quei moduli che, in pieno silenzio, eseguono tutti i calcoli numerici senza fornire nessun genere di output visibile durante il procedimento. Perciò, quando l'elaborazione è lunga ed impegnativa, per non dare l'impressione di un blocco del software, mentre in realtà tutto procede regolarmente, viene segnalata in una finestra a video la procedura di calcolo in corso ed il tempo rimasto per la conclusione del programma.

Per la realizzazione di un processore che, in altri termini, potrebbe essere definito come un "motore" di calcolo numerico, il Free Pascal sembra, a tutti gli effetti, un linguaggio dedicato a questo genere di software. Basterebbe citare solo tre fatti:

  1. il Free Pascal è un linguaggio compilato ed altamente modulare;
  2. il Free Pascal utilizza tutti i tipi di dati numerici: interi dalla lunghezza di un byte fino ad otto, cioè interi a 64 bit; reali con singola precisione, doppia ed estesi;
  3. gli array statici e dinamici sono nativi del linguaggio, facilmente gestibili e possono essere di qualsiasi tipo e dimensioni.

Anche se ci sono altri punti a favore del Free Pascal, questi sono, fondamentalmente, quelli che lo rendono pari al C/C++ ed al Fortran per quanto riguarda lo sviluppo di applicazioni numeriche.

Il calcolo matriciale

Il calcolo matriciale è indispensabile per l'elaborazione numerica e la risoluzione di una larga gamma di problemi. Molti fenomeni scientifici, applicazioni tecniche ed economiche hanno come modello matematico un sistema di equazioni lineari. Non è un caso che il Fortran, nel suo design originale, prevede solo l'utilizzo delle matrici come unico tipo di dato strutturato. Il Free Pascal offre una gestione nativa e completa per le matrici (array).

Quando le dimensioni di un vettore o di una matrice sono note conviene sempre dichiarare un array statico per ragioni di efficienza e senza l'utilizzo di puntatori. Ecco un esempio:

const
  MAX_STUDENTI = 100;

type
   // dichiara un vettore con 10 interi
   Array10Interi = array[1..10] of integer; 
   // dichiara un vettore con 10 reali con doppia precisione
   Array10Double = array[1..10] of double; 
   str80 = string[80];
   // dichiara 10 stringhe di tipo str80 (con 80 caratteri)
   Array10Str80 = array[1..10] of str80;
   // dichiara una matrice quadrata di interi (10x10)   
   Array10x10Int = array[1..10,1..10] of integer;
   // dichiara una matrice rettangolare di interi con 10 linee e 100 colonne;   
   Array10x100Int = array[1..10,1..100] of integer; 

  StudenteRec = record
    nome: str80;
    matricola: integer;
    e_mail: str80;
  end;

NumeroComplessoRec = record
  parte_reale: double;
  parte_immaginaria: double;
  end;

ArrayStudenti = array[1..MAX_STUDENTI] of StudenteRec;
ArrayNumeriComplessi = array[1..1000] of NumeroComplessoRec;

La dichiarazione di alcune variabili dei tipi precedentemente definite potrebbe essere la seguente:

MyInts: Array10Interi;
Studenti: ArrayStudenti;
Complessi: NumeroComplessoRec;
E' possibile ora assegnare dei valori alle variabili compatibili con il loro tipo di dato.
for i := 1 to 10 do
  MyInts[i] := random(100);
Studenti[1].nome  := 'Mario Rossi';
Studenti[1].matricola  := 123456;
Studenti[1].e_mail  := 'mariorossi@mariorossi.com';

Un modo elegante di dichiarare una matrice è quello basato su indici tipizzati. 
Ecco un esempio:
type
  colors = (rosso,verde,blu);
  array_colors = array[rosso..blu] of byte;
var
  rgb: array_colors;

begin
  rgb[rosso] := 184;
  rgb[verde] := 86;
  rgb[blu] := 114;
end.

Il grosso problema degli array statici è che una volta che è stata fissata la loro lunghezza è impossibile modificarla in seguito. Perciò, se le dimensioni di un array non sono note, o sono variabili, conviene passare agli array dinamici che offrono maggior flessibilità.

Ecco alcune dichiarazioni di array dinamici.

type
   // dichiara un vettore dinamico di interi
   ArrayInteri = array of integer; 
   
   // dichiara un vettore dinamico di reali con doppia precisione
   ArrayDouble = array of double; 
   
   str80 = string[80];
   
   // dichiara un vettore di stringhe di tipo str80
   ArrayStr80 = array of str80; 
   
   // dichiara una matrice bidimensionale di interi
   ArrayArrayInt = array of array of integer; 
   ArrayStudenti = array of StudenteRec;

Le variabili sono dichiarabili in modo del tutto identico a quello precedente. L'unica differenza sta nell'inizializzazione di un array dinamico. Ecco come avviene.

var
  MyInts: ArrayInteri;
  AAI: ArrayArrayInt;

SetLength(MyInts,10); // crea un array con 10 elementi

Attenzione però per l'indice che varia da 0 a 9; cioè il primo elemento è MyInts[[0] mentre l'ultimo è MyInts[9]. Esattamente come avviene in C/C++.
E' possibile modificare in qualsiasi istante le dimensioni di un array dinamico con l'istruzione "SetLength". Per esempio:

// stabilisce la nuova dimensione a 100 elementi
SetLength(MyInts,100);
// elimina dalla memoria l'intero array
SetLength(MyInts,0); 

Un array bidimensionale viene creato e distrutto allo stesso modo.
// crea una array bidimensionale di interi con 100 linee e 100 colonne
SetLength(AAI,100,10); 
// assegna al primo elemento il valore 1;
AAI[0,0] := 1; 
// assegna all'ultimo primo elemento il valore 1000;
AAI[99,99] := 1000; 
SetLength(AAI,0,0); // distrugge l'array

Come abbiamo visto, senza nessun utilizzo esplicito dei puntatori, possiamo creare e modificare la lunghezza di array dinamici con estrema semplicità senza ricorrere a nessuna libreria esterna. La gestione di array dinamici in C/C++ rappresentano un vero incubo per il programmatore non esperto.

Ecco una completa procedura che risolve sistemi di equazioni lineari col metodo di eliminazione di Gauss.

const maxr = 30;
 maxc = 30;

type ary = array[1..maxr] of real;
 arys = array[1..maxc] of real;
 ary2s = array[1..maxr,1..maxc] of real;

procedure gauss
        (a : ary2s;
  y : arys;
     var coef : arys;
  ncol : integer;
     var error : boolean);

var
 b : ary2s; //matrice di lavoro
 w : arys;  // vettore di di lavoro
 i,j,i1,k,
 l,n : integer;
 hold,sum,
 t,ab,big: real;

begin
  error:=false;
  n:=ncol;
  for i:=1 to n do
  begin
    for j:=1 to n do
    //copia la matrice dei coefficienti nella matrice di lavoro
       b[i,j]:=a[i,j]; 
    //copia il vettore dei termini noti nel vettore di lavoro
    w[i]:=y[i]; 
  end;
  for i:=1 to n-1 do
  begin
    big:=abs(b[i,i]);
    l:=i;
    i1:=i+1;
    for j:=i1 to n do
    begin  //trova il termine più grande della colonna
      ab:=abs(b[j,i]);
      if ab>big then
      begin
        big:=ab;
        l:=j
      end;
    end;
    if big=0.0 then error:= true
    else
    begin
      if l<>i then
      begin
     //scambia le linee in modo che il terminie più grande sia sulla diagonale
        for j:=1 to n do
        begin
          hold:=b[l,j];
          b[l,j]:=b[i,j];
          b[i,j]:=hold
        end;
        hold:=w[l];
        w[l]:=w[i];
        w[i]:=hold
      end; { if l<>i }
      for j:=i1 to n do
      begin
        t:=b[j,i]/b[i,i];
        for k:=i1 to n do
          b[j,k]:=b[j,k]-t*b[i,k];
          w[j]:=w[j]-t*w[i]
      end;
    end;
  end;
  if b[n,n]=0.0 then error:=true
  else
  begin
    coef[n]:=w[n]/b[n,n];
    i:=n-1;
    //sostituzione all'indietro
    repeat
      sum:=0.0;
      for j:=i+1 to n do
        sum:=sum+b[i,j]*coef[j];
      coef[i]:=(w[i]-sum)/b[i,i];
      i:=i-1
    until i=0
  end; { if b[n,n]=0 }
  if error then writeln(chr(7),'ERROR: Matrix is singular')
end; { GAUSS }

Questo breve e semplice codice permette di risolvere un gran numero di problemi che hanno come modello matematico un sistema di equazioni lineari.

La statistica inferenziale (1)

La statistica inferenziale è una recente disciplina scientifica, figlia diretta della matematica, che a partire da alcuni dati campionari ci permette di capire la tendenza di un fenomeno fisico, economico, sociale, economico, politico... La statistica è largamente utilizzata in molti settori e ha fornito precise informazioni su alcuni fenomeni difficilmente comprensibili utilizzando altri strumenti. La statistica è considerata un potentissimo mezzo scientifico per la risoluzione di alcuni tipi di problemi non deterministici quando non esiste la possibilità di risolverli direttamente applicando delle formule matematiche e si dispone solo di alcuni dati sul fenomeno.

I programmi software di statistica inferenziale richiedono di essere scritti in linguaggi molto efficienti. La ragione sta nel fatto che, oltre ad accedere a grandi quantità di dati, generalmente contenuti in database relazionali, utilizzano algoritmi di calcolo numerico molto impegnativi; perciò, serve una grande potenza di calcolo per l'elaborazione dei dati e la visualizzazione grafica dei risultati.

Il Free Pascal, insieme al C++, costituiscono oggi gli strumenti più validi, versatili e a buon mercato per realizzare queste applicazioni. Il vantaggio del Free Pascal sul C++ è la minor complessità e la semplicità.

Il FEM

Il FEM (Finite Element Method), o metodo agli elementi finiti, rappresenta oggi uno degli strumenti più avanzati e sofisticati per la risoluzione di molti problemi fisici ed ingegneristici. Il FEM è applicabile per la risoluzione di problemi di elettromagnetismo, di calcolo strutturale, di idraulica, aerodinamica, meccanica dei fluidi e molti altri. Si tratta di un metodo numerico altamente standardizzato ma poco conosciuto ed applicato. Sono passati quasi 60 anni da quando il FEM fu inventato ma è rimasto per tanti anni una pura elucubrazione matematica perché per essere applicato richiedeva l'uso di un computer. Il calcolo manuale agli elementi finiti è stato sempre improponibile perché richiedeva tempi molto lunghi per semplici problemi. Oggi le cose stanno cambiando radicalmente e l'utilizzo del FEM, merito all'hardware sempre più potente e più economico, sta diventando popolare tra tecnici e ricercatori.

Grazie al FEM, si potrebbe, per esempio, dividere una grande struttura in piccole strutture connesse che vengono di seguito risolte singolarmente; la soluzione globale viene "assemblata" a partire da quelle parziali delle singole strutture.

Le applicazioni FEM mettono a dura prova hardware, linguaggi, programmatori e tecnici per la mole di calcolo e la grande varietà di dati e considerazioni scientifiche e tecniche. Basti pensare ai programmi di calcolo strutturale di grattacieli in zone ad alto rischio sismico. Anche se, concettualmente, la cosa è abbastanza semplice da capire, in termini di elaborazione numerica e di simulazione grafica il problema è complesso e molto impegnativo.

Anche per lo sviluppo di applicazione FEM il Free Pascal è un ottimo strumento soprattutto quando ci rendiamo conto che la risoluzione di un problema con il FEM si traduce, matematicamente parlando, in prevalenza in un semplice calcolo matriciale!

Free Pascal e OpenGL

OpenGL è la libreria grafica più nota ed utilizzata al mondo; ormai viene implementata a livello hardware in tutte le schede grafiche di ultima generazione ed è presente su quasi tutti i sistemi operativi e perciò, molto probabilmente, è già installata sul vostro sistema e pronta per l'uso. E' una libreria matura, collaudata ed ampiamente utilizzata per il rendering nella maggior parte dei sistemi CAD, di simulazione grafica 3D e negli effetti speciali. Essendo scritta nel linguaggio C, tutte le funzioni OpenGL sono accessibili dal Free Pascal come avviene esattamente dal C. Basta solo includere la unit "gl.pp" che fa parte della distribuzione standard del Free Pascal nel vostro programma e chiamare direttamente le funzioni OpenGL.

Essendo OpenGL e il Free Pascal multipiattaforma, i vostri programmi non hanno bisogno di nessuna modifica al codice sorgente portandoli da un sistema operativo ad un altro. Con una semplice compilazione del codice sulla nuova piattaforma avrete un programma nativo che gira efficientemente alla massima velocità.

Il Free Pascal è fornito di alcuni esempi molto interessanti per iniziare; per l'approfondimento vi consiglio di visitare il sito web indicato a fine articolo.

Conclusione

Il Pascal è uno dei pochi linguaggi standardizzati (ISO e ANSI), ampiamente utilizzato da più di trenta anni in quasi tutte le facoltà scientifiche e tecniche del mondo nonché in alcune industrie e entri di ricerca. Basti pensare al CERN dove il Pascal è onnipresente. Questo ultimo fatto non stupisce; il Pascal è stato da sempre il linguaggio prediletto dai fisici. Applicazioni, codice, libri, pubblicazioni e siti internet dedicati al linguaggio sono così diffusi che è quasi impossibile solo stimarli. In poche parole si tratta di una grande eredità da non sottovalutare o trascurare.

Se il vostro prossimo progetto è quello di scrivere un'applicazione scientifica o tecnica utilizzando un unico linguaggi per tutti i "reparti", allora il Free Pascal è un eccellente candidato che offre tutto quello che vi serve. Linguaggio, compilatore, linker, librerie ed ambiente di sviluppo sono gratuitamente disponibili in codice sorgente e compilato. Il tutto può essere utilizzato senza particolari limiti per sviluppare software di ogni genere compreso quello chiuso e commerciale.

C'è da aggiungere la disponibilità di una completa documentazione sul linguaggio ed il suo compilatore a corredo che permette a chiunque di imparare il Free Pascal senza spendere un centesimo.

A concludere questa serie di articoli sono i miei ringraziamenti alla comunità sparsa per tutto il mondo impegnata nello sviluppo di software libero e gratuito, ed in modo particolare a quella del Free Pascal.

Sito ufficiale di Lua: www.freepascal.org


(1): La statistica inferenziale fornisce delle informazioni su un fenomeno e non delle soluzioni di un determinato problema. Informazione = Dati + Elaborazione Statistica. La statistica viene spesso definita come la scienza che fa "parlare i dati".