Informatica di base: procedure e funzioni
✍ Scritto: Giansante Gabriele, 2001
- Procedure e funzioni
- Passaggio parametri
- Ricorsione
Chi si avvicina per la prima volta al mondo della programmazione
non sempre riesce a familiarizzare subito con i concetti di procedura e di funizione.
Questo piccolo testo vuole introdurre l'argomento, presentando anche il concetto di ricorsione,
in informatica strettamente legato al concetto di funzione.
Procedure e funzioni
Un programma puo' certamente essere realizzato
scrivendo una sequenza di istruzioni che termina con il programma stesso.
La soluzione non e' malvagia, ma si presta
ad innumerevoli svantaggi. Il primo fra tutti e' l'impossibilita'
di gestire progetti di grosse dimensioni: finche' si tratta di correggere
o di modificare codice di un migliaio di righe non ci sono problemi, ma
quando il codice raggiunge dimensioni dell'ordine di 100.000 righe ed oltre...
Tutti i linguaggi ad alto livello consentono di
programmare in modo piu' elegante ed efficace attraverso le astrazioni
mediante procedure e funzioni.
Queste astrazioni consentono di creare unita'
ben distinte all'interno di un programma raggruppando una sequenza di istruzioni,
dandole un nome e stabilendo le modalita' di comunicazione con il resto
del programma. Queste istruzioni, generalmente, vengono raggruppate tenendo
conto del compito da svolgere, anche se qualsiasi criterio e' permesso.
Il programmatore, per usare questa sequenza di
istruzioni, avra' bisogno solamente di indicarne il nome, al resto pensera'
il compilatore o il traduttore del linguaggio.
Si ha un'astrazione della nozione di istruzione
(la procedura) e di operatore (la funzione). Infatti, la procedura
non e' altro che una istruzione complessa che puo' essere utilizzata ovunque
possa una istruzione semplice: il compito principale di una procedura e'
il modificare il contenuto di locazioni di memoria. La funzione,
invece, puo' essere utilizzata in qualunque valutazione di espressione:
ha il compito di fornire un valore, anche se non e' escluso che, nel fornirlo,
assuma pure i comportamenti della procedura.
Come le istruzioni e gli operatori, anche le procedure
e le funzioni possono accettare degli argomenti (parametri).
Nel paragrafo successivo verra' mostrato come.
Infine, e' stato detto che la funzione e' un'astrazione
del concetto di operatore. Ma un operatore, la maggior parte delle volte,
e' sovraccarico, cioe' e' definito su piu' tipi di dato diversi, piu' o
meno simili. Purtroppo, non tutti i linguaggi di programmazione ad alto
livello, permettono il sovraccarico delle funzioni (al posto di sovraccarico
si parla overloading delle funzioni),
cioe', per alcuni linguaggi, una funzione non puo' essere definita piu'
volte all'interno di un programma per diversi tipi di argomenti.
Passaggio parametri
Come visto nella sezione precedente, per la sequenza
di istruzioni che realizza una procedura o una funzione, viene stabilita
la modalita' di comunicazione con il resto del programma. I parametri servono
alla comunicazione esplicita, ed interna, di dati e risultati fra procedura
o funzione (chiamato)
ed il resto del programma (chiamante,
puo' essere anche un'altra funzione e procedura). Dal lato del codice "chiamato",
si parla di parametri formali. Dal lato "chiamante", si parla di parametri
attuali.
I parametri formali
sono degli identificatori speciali dichiarati localmente al chiamato. L'essere
speciale deriva dall'associazione che viene fatta tra essi ed i parametri
attuali, nel meccanismo di passaggio dei parametri. I parametri
attuali possono rappresentare dei valori
o degli identificatori esterni al chiamato.
Vediamo un esempio.
void A(int x, int y)
{
...
}
void B()
{
...
int h=4;
...
A(h,5);
...
}
Nel codice
mostrato, la funzione B chiama la A. In questo processo di chiamata, "h"
e "5" sono i parametri attuali, mentre invece "x" ed "y" sono i parametri
formali.
Il meccanismo di passaggio non sempre e' lo stesso:
esistono diverse tecniche.
Passaggio per valore:
E' il modo piu' semplice per passare parametri:
i parametri attuali vengono valutati (ne viene calcolato ed estratto il
valore) ed i valori sono associati ai parametri formali.
Nel codice precedente, "x" assumera' il valore
di "h" ed "y" assumera' il valore "5". Saranno comunque tutte entita' diverse,
cioe' i valori dei parametri attuali non verranno alterati in alcun modo.
Passaggio per riferimento (o per indirizzo):
Viene passato l'indirizzo della memoria contenente
i valori dei parametri attuali. Quindi, quest'ultmi, condivideranno l'indirizzo
con i parametri formali. Ogni modifica fatta ai formali, si riflettera'
sugli attuali.
In alcuni linguaggi, se il parametro passato
per riferimento non e' un identificatore ma una espressione (puramente
numerica o composta da identificatori), allora viene valutata l'espressione
ed il valore calcolato viene posto in una nuova locazione di memoria. L'indirizzo
di questa nuova locazione di memoria sara' quello passato.
Metodo "Copia-Ripristina"
E' un ibrido dei due metodi precedenti. L'associazione
e' identica al passaggio per valore, quindi i parametri formali verranno
modificati indipendentemente da quelli attuali. Alla fine della procedura
o della funzione, pero', il valore dei parametri formali viene copiato
in quelli attuali.
Questo metodo potrebbe essere utile nella programmazione
parallela, dove una funzione continua ad accedere ad un identificatore
mentre viene modificato da una procedura appena chiamata. Finche' il chiamato
non avra' terminato il proprio lavoro, il chiamante usera' il valore vecchio
e non uno inconsistente.
Ricorsione
La ricorsione e' una tecnica altamente inefficiente
in termini di memoria utilizzata e di tempo impiegato nell'esecuzione,
a causa delle modalita' di implementazione nei vari linguaggi. Ha il notevole vantaggio,
pero', di introdurre nei programmi eleganza, naturalezza descrittiva, facilita'
di espressione per problemi particolari, sinteticita'.
In matematica, una funzione
e' definita ricorsivamente quando, nella
sua definizione, chiama se stessa.
Un esempio puo' essere la funzione fattoriale
f(x)=x!
(es. 5!=5*4*3*2*1=120).
Per definizione il fattoriale vale 1 se
x=0, vale x*f(x-1) se x>0 (5!=5*4!=5*4*3!=5*4*3*2!=5*4*3*2*1!=5*4*3*2*1).
Come si puo' osservare, la definizione avviene
in due passi distinti: si ha un passo base ed un passo induttivo.
Il passo base
consiste nella "condizione di blocco" della funzione. Nella funzione precedente,
come in tutte le altre funzioni ricorsive, il passo base consiste nel dare
un valore indipendente dalla variabile, sotto certe condizioni. L'utilita'
del passo base sta nell'evitare alla funzione di essere applicata infinite
volte (oppure, nel caso del fattoriale, di arrivare ad un punto in cui
la funzione non e' definita, rendendo senza senso la formula).
Il passo induttivo
consiste nella definizione della funzione nel caso generale (in termini
della funzione stessa).
Come ulteriore esempio, vediamo la realizzazione,
in Java, della funzione che calcola l'elevamento a potenza (positiva) di
una base (positiva anch'essa).
La funzione matematica e' f(x,y)=x^y.
Puo' essere definita ricorsivamente come 1
per y=0 e
x*f(x,y-1) per y>0.
Il seguente codice realizza la funzione:
long Eleva(int x, int y)
{
if (y==0) return 1;
else return x*Eleva(x,y-1);
}
Hai trovato utile questo articolo?
Aiutami a condividerlo o metti un "mi piace".
Grazie mille!