Procedure e Funzioni in Object Pascal

In questa lezione parleremo delle funzioni e delle procedure in linguaggio Object Pascal. Questi concetti sono fondamentali per la programmazione e permettono di creare codice modulare, organizzato e facilmente manutenibile. Le funzioni e le procedure sono utilizzate per eseguire determinate attività all'interno del programma e possono essere riutilizzate in più punti del codice.

Le funzioni sono routine di codice che eseguono una specifica attività e restituiscono un valore. Possono accettare parametri e sono utilizzate per eseguire calcoli, elaborazioni e qualsiasi altra attività che richieda il ritorno di un valore. Le procedure, invece, non restituiscono alcun valore ma possono accettare parametri. Sono utilizzate per eseguire azioni, come la stampa di un messaggio o la lettura di un input dall'utente.

In questa lezione ci concentreremo sulle basi di funzioni e procedure: come crearle e come utilizzarle. Nelle prossime lezioni vedremo come passare parametri.

Procedure e Funzioni

Le procedure e le funzioni in object pascal sono utilizzate per rendere il codice modulare. Attraverso di esse è possibile scrivere una sola volta porzioni di codice e richiamarle da più punti del nostro programma. In gergo tecnico queste porzioni di codice prendono il nome di routine.

Il linguaggio Object Pascal enfatizza la differenza tra due diversi tipi di routine:

  • Funzione: è una computazione che restituisce un risultato;
  • Procedura: è una serie di operazioni da svolgere in sequenza.

In molti linguaggi di programmazione la differenza tra le due è abbastanza labile e dal punto di vista della sintassi non c'è molta differenza. In Object Pascal, invece, la differenza è enfatizzata dalla sintassi differente.

Procedure

Per definire una procedura in object pascal è necessario utilizzare la parola chiave procedure seguita dal nome della procedura e dal suo corpo, ossia dalla sequenza di operazioni da svolgere, racchiuso tra begin ed end:

procedure Nome;
begin
    { Corpo della procedura }
end;

Supponiamo, ad esempio, di voler scrivere una procedura che stampi a schermo un messaggio:

procedure StampaMessaggio;
begin
    WriteLn('Ciao!');
end;

A questo punto possiamo richiamare la procedura in più punti del nostro programma in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
program EsempioProcedure;

procedure StampaMessaggio;
begin
    WriteLn('Ciao!');
end;

begin
    StampaMessaggio;
    WriteLn('Benvenuto');
    WriteLn('Arrivederci');
    StampaMessaggio;
end.

La procedura StampaMessaggio viene richiamata nel corpo del nostro programma alle righe 9 e 12. Per richiamare una procedura, o più precisamente per invocare una procedura, basta inserire il nome della procedura stessa seguita da un punto e virgola.

Compilando ed eseguendo il nostro programma otteniamo il seguente risultato:

Ciao!
Benvenuto
Arrivederci
Ciao!

La potenza delle procedure sta nel fatto che il codice può essere scritto una sola volta e può essere invocato più volte in vari punti del nostro programma. Ad esempio, se volessimo modificare il messaggio stampato a schermo dalla procedura basta modificare il programma in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
program EsempioProcedure;

procedure StampaMessaggio;
begin
    WriteLn('Buongiorno!');
end;

begin
    StampaMessaggio;
    WriteLn('Benvenuto');
    WriteLn('Arrivederci');
    StampaMessaggio;
end.

Se compiliamo di nuovo il nostro programma e lo eseguiamo otteniamo il seguente risultato:

Buongiorno!
Benvenuto
Arrivederci
Buongiorno!

Il programma dell'esempio è di scarsa utilità ma serve a introdurre la potenza delle procedure.

Ricapitolando:

Definizione

Procedure in Object Pascal

Una Procedura è una porzione di codice che esegue una specifica attività e può essere richiamata o invocata da un punto qualunque del codice.

La sintassi per definire una procedura è la seguente:

procedure nome_procedura;
begin
    { Corpo Procedura }
end;

Per invocare una procedura basta inserire il nome della procedura stessa nel punto di invocazione.

Funzioni

Rispetto alle procedure, le funzioni restituiscono un risultato. In altre parole, una funzione effettua delle operazioni e restituisce il risultato di tali elaborazioni.

La sintassi per definire una funzione è la seguente:

function NomeFunzione: tipo_di_ritorno;
begin
    { Corpo della funzione }
end;

Dunque, per definire una funzione è necessario usare la parola chiave function seguita dal nome della funzione a cui bisogna aggiungere il tipo del valore di ritorno dopo i due punti. Successivamente, bisogna specificare il corpo della funzione tra begin ed end.

Come abbiamo detto, una funzione restituisce un valore. Questa caratteristica viene imposta dal compilatore che, in caso contrario, ci segnala un warning.

Ad esempio, supponiamo di avere la seguente funzione:

function Due: Integer;
begin
    { Non fa nulla }
end;

Se proviamo a compilare un file sorgente contenente questa definizione otteniamo il seguente warning:

test.pas(5,10) Warning: Function result does not seem to be set

In parole povere, il compilatore ci sta segnalando che la nostra funzione non restituisce alcun risultato.

A questo punto sorge spontanea la domanda: come possiamo specificare il valore di ritorno di una funzione? Esistono due modi: il modo classico e il modo moderno.

Per specificare il valore di ritorno di una funzione usando il modo classico basta assegnare al nome della funzione il valore. In altre parole, internamente una funzione definisce una sorta di variabile che ha il suo stesso nome e che rappresenta il valore di ritorno. Ritornando all'esempio di prima, la nostra funzione può essere scritta in questo modo:

// Modo classico

// La funzione Due restituisce il valore 2
function Due: Integer;
begin
    Due := 2;
end;

Il modo moderno di specificare il valore di ritorno di una funzione è uguale al modo classico, l'unica differenza è che il valore di ritorno è rappresentato da una variabile che ha il nome di Result anziché del nome della funzione. Per cui possiamo scrivere la funzione Due in questo modo:

// Modo Moderno

// La funzione Due restituisce il valore 2
function Due: Integer;
begin
    Result := 2;
end;

Usando la parola chiave Result il codice diventa molto più leggibile in quanto è più esplicito il valore di ritorno.

Cosa importante da tenere a mente è che le funzioni possono restituire un unico risultato. In Object Pascal è possibile restituire più di un risultato ma lo vedremo nelle prossime lezioni.

A questo punto, possiamo utilizzare la nostra funzione in un programma in questo modo:

program EsempioFunzione;

// La funzione Due restituisce il valore 2
function Due: Integer;
begin
    Result := 2;
end;

var
    I, X: Integer;
begin
    for I := 1 to 10 do
    begin
        X := Due * I;
        WriteLn(X);
    end;
end.

Il programma stampa i primi 10 multipli di 2.

Ovviamente una funzione come Due ha poca utilità in quanto avremmo potuto realizzare il programma in maniere più semplice sostituendo alla chiamata alla funzione il numero 2. Tuttavia nelle prossime lezioni vedremo come passare dei parametri alle nostre funzioni in maniera tale da realizzare funzioni molto più utili.

Ricapitolando:

Definizione

Funzioni in Object Pascal

In Object Pascal una Funzione è una porzione di codice, o meglio una routine, che esegue una specifica attività e restituisce un risultato.

La sintassi per definire una funzione è la seguente:

function nome_funzione: tipo_ritorno;
begin
    { Corpo Funzione }
end;

Per restituire un risultato si può usare la sintassi classica:

    nome_funzione := valore_di_ritorno;

oppure la sintassi moderna:

    Result := valore_di_ritorno;

Per invocare una funzione basta inserire il nome della funzione nel punto in cui si vuole usarla. Nel caso delle funzioni è possibile utilizzare il valore di ritorno in espressioni o assegnamenti.

Dichiarazioni anticipate

Nel paragrafo precedente abbiamo visto come definire funzioni e procedure. Abbiamo visto che per invocare una procedura o una funzione è sufficiente inserire il loro nome nel punto in cui vogliamo richiamarle. In altre parole, dobbiamo inserire il loro identificativo.

Nel dire questo, tuttavia, abbiamo omesso un dettaglio fondamentale ossia che il compilatore deve conoscere già un identificativo quando lo incontra.

Bisogna tenere a mente, infatti, che il compilatore legge il codice sorgente di un programma dall'alto verso il basso per cui, quando incontra un identificativo, esso si aspetta di averlo già incontrato prima. Supponiamo, ad esempio, di dare in pasto al compilatore il seguente programma:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
program prova;

procedure MiaProcedura;
begin
    { Codice della procedura }
end;

begin
    { Codice... }
    MiaProcedura;
    { Codice... }
end.

In tal caso, quando il compilatore arriva alla riga 10 e incontra l'identificativo MiaProcedura non avrà nessun problema a capire che si tratta della procedura scritta sopra.

Supponiamo, adesso, che la procedura MiaProcedura richiami a sua volta un'altra procedura: AltraProcedura. Potremmo scrivere il nostro programma in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
program prova;

procedure MiaProcedura;
begin
    AltraProcedura;
    AltraProcedura;
end;

procedure AltraProcedura;
begin
    WriteLn('Altra Procedura');
end;

begin
    MiaProcedura;
end.

Se proviamo a compilare il programma di sopra otteniamo i seguenti errori:

prova.pas(5,5) Error: Identifier not found "AltraProcedura"
prova.pas(6,5) Error: Identifier not found "AltraProcedura"

Questo perché, quando il compilatore raggiunge la riga 5 e la riga 6 si trova di fronte all'identificativo AltraProcedura che non conosce. Infatti AltraProcedura è definita dopo alla riga 9.

Per risolvere questo problema abbiamo due possibilità. La prima e più semplice consiste nel riscrivere il programma in modo tale da riposizionare la definizione di AltraProcedura prima della definizione di MiaProcedura. Ma questo non sempre è possibile e in alcuni casi è impossibile come vedremo nella lezione sulla ricorsione.

La seconda possibilità prende il nome di Dichiarazione anticipata (dall'inglese Forward Declaration). In sostanza si tratta di avvertire il compilatore dell'esistenza di un identificatore, nel nostro caso di una procedura o funzione, senza definirne la struttura.

Nel caso di procedure o funzioni, la dichiarazione anticipata consiste nello specificare il nome della funzione o della procedura seguita dalla parola chiave forward ma senza definirne il corpo, in questo modo:

// Dichiarazione anticipata di una procedura
procedure NomeProcedura; forward;

// Dichiarazione anticipata di una funzione
function NomeFunzione: tipo_di_ritorno; forward;

Usando le dichiarazioni anticipate possiamo riscrivere il programma di sopra in questo modo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
program prova;

procedure AltraProcedura; forward;

procedure MiaProcedura;
begin
    AltraProcedura;
    AltraProcedura;
end;

procedure AltraProcedura;
begin
    WriteLn('Altra Procedura');
end;

begin
    MiaProcedura;
end.

In questo esempio, nella riga 3 abbiamo dichiarato attraverso una forward declaration l'esistenza di una procedura AltraProcedura. In questo modo, nelle righe 7 e 8 il compilatore, quando ne incontra l'identificatore, ne conosce l'esistenza e non riporta errori.

Ricapitolando:

Definizione

Dichiarazioni Anticipate (Forward Declaration)

In Object Pascal una Dichiarazione Anticipata di una funzione o procedura (in inglese Forward Declaration) consiste nel dichiararne l'esistenza senza definirne il corpo.

In questo modo è possibile inserire l'invocazione della funzione o procedura nel codice prima ancora che il compilatore abbia esaminato la definizione.

La sintassi per una dichiarazione anticipata consiste nella dichiarazione della funzione o procedura seguita dalla parola chiave forward ma senza il corpo:

procedure nome_procedura; forward;
function nome_funzione: tipo_di_ritorno; forward;

In Sintesi

Questa lezione introduttiva ci ha permesso di conoscere le basi delle Procedure e Funzioni nel linguaggio Object Pascal. Abbiamo visto come definire e invocare una funzione o una procedura.

La peculiarità del linguaggio Object Pascal sta nel fatto che la sintassi esplicita la differenza tra le due imponendola al programmatore a differenza di altri linguaggi. Per cui una funzione restituirà sempre un risultato a differenza di una procedura.

Nella prossima lezione vedremo come passare informazioni alle nostre procedure e funzioni in maniera tale che possano lavorare su di esse.