Sei sulla pagina 1di 6

Corso di Visual Basic

(Parte 9)

di Maurizio Crespi

La nona lezione del corso dedicato alla programmazione in Visual Basic si pone lo scopo
di illustrare le funzioni definibili dall'utente e il concetto di ricorsione
Nella scorsa lezione sono state descritte le subroutine e sono stati evidenziati i benefici
che il loro uso d al programmatore. Questo mese l'argomento sar ulteriormente
approfondito con l'introduzione delle funzioni, ovvero di procedure in grado di restituire un
valore senza l'ausilio di una variabile globale o di un parametro passato per riferimento,
nonch con la descrizione dell'uso avanzato che possibile fare di esse. Prima per
opportuno compiere un passo indietro per riportare alla mente i concetti esposti nella
precedente puntata del corso. Come sempre, il fine raggiunto mediante l'illustrazione
delle soluzioni degli esercizi in essa proposti.

Le soluzioni degli esercizi della scorsa lezione


Si tratta di due semplicissimi esercizi, il cui scopo quello di aiutare il lettore a riconoscere
la differenza fra la modalit di passaggio dei parametri per valore e quella per riferimento.
Primo esercizio
Il primo esercizio prevede la realizzazione di una procedura in grado di restituire il valore
assoluto di un numero intero per mezzo di un parametro passato per riferimento.
Dai testi scolastici noto che il valore assoluto di un numero pari ad esso se positivo
oppure, in caso contrario, al suo opposto. Volendo fare a meno di ricorrere alla funzione
Abs prevista dalla libreria standard, possibile effettuare il calcolo per mezzo di una
struttura If. La soluzione dell'esercizio pertanto la seguente:
Sub ValAssoluto(ByVal Numero As Double, ByRef Risultato As Double)
If Numero > 0 Then
Risultato = Numero
Else
Risultato = -1 * Numero
End If
End Sub
Si noti che il numero di cui deve essere calcolato il valore assoluto fornito alla procedura
tramite un parametro passato per valore.
Si supponga ora di voler eseguire la seguente porzione di codice:
r=0
n=45
ValAssoluto n, r
MsgBox(Str$(r))
Un eventuale tentativo da parte della routine di modificare il dato fornitole come primo
parametro non ha effetto sulla variabile n posta nel blocco di codice chiamante. Diverso
invece il discorso per quanto riguarda la variabile r. Essa, infatti, passata come
parametro per riferimento; la procedura pu quindi intervenire direttamente sul suo valore.
Ci permette alla routine di restituire un dato senza far uso di variabili globali.

Secondo esercizio
Il secondo esercizio richiede la realizzazione di una procedura in grado di richiamare
quella appena illustrata per calcolare la somma dei valori assoluti di 3 numeri passati
come parametri per valore. La soluzione banale ed rappresentata dal codice riportato
di seguito:
Sub SommaVAssoluti(ByVal n1 As Double, ByVal n2 As Double, ByVal
n3 As Double, ByRef Somma As Double)
Dim r1 As Double
Dim r2 As Double
Dim r3 As Double
ValAssoluto n1, r1
ValAssoluto n2, r2
ValAssoluto n3, r3
Somma = r1 + r2 + r3
End Sub
Anche in questo caso, il risultato restituito al blocco di codice chiamante per mezzo di un
parametro passato per riferimento.

Le funzioni
Un sensibile miglioramento della leggibilit del codice e della comodit d'uso dello
strumento di sviluppo deriva dalla possibilit di restituire un valore evitando l'uso di una
variabile globale o di un parametro passato per riferimento. Ci possibile per mezzo
delle funzioni. Il linguaggio prevede una quantit elevatissima di funzioni standard. Ad
esempio, per calcolare il valore assoluto di un numero, esiste la funzione Abs, che riceve
in ingresso un dato numerico e ne restituisce il modulo. La riga
x = Abs(y)
fa s che alla variabile x sia assegnato il valore assoluto del numero contenuto nella
variabile y. Come accade per le procedure, possibile incrementare l'insieme delle
funzioni disponibili creandone ad hoc per soddisfare le proprie esigenze. A tal fine
necessario racchiudere le istruzioni in strutture dichiarate per mezzo della parola chiave
Function, secondo la sintassi di seguito riportata:
[Public|Private] Function <nome> [(<definizione_parametro_1>,
... <definizione_parametro_n>)] As <tipo>
[<dichiarazione_variabili_locali>]
<istruzione_1>
...
<istruzione_n>
<nome>=<valore>
End Function
Com' possibile notare, l'analogia con le procedure notevole. A differenza di esse,
necessario indicare un tipo di dati standard al termine della riga di dichiarazione. Esso
identifica il formato in cui deve essere restituito il risultato. inoltre necessario fare in
modo che all'interno del blocco di codice sia presente una riga che preveda
l'assegnamento di un valore a una variabile avente lo stesso nome della funzione. Tale
dato quello restituito al blocco chiamante.

Si supponga di voler realizzare una semplice funzione in grado di moltiplicare un numero


intero per 4. Il codice necessario il seguente:
Function Quadruplo(Numero As Integer)
Quadruplo = Numero * 4
End Function
Il valore assunto dal parametro Numero moltiplicato per 4 e restituito dalla funzione.
possibile utilizzare la struttura appena creata in un'istruzione di assegnamento scrivendo
il suo nome seguito dai parametri posti fra parentesi tonde, come nell'esempio che segue,
in cui il risultato della valutazione della funzione Quadruplo assegnato alla variabile x.
x = Quadruplo(y)
Lo stesso scopo pu essere ottenuto anche per mezzo di una procedura dotata di un
parametro passato per riferimento. In questo caso il codice il seguente:
Sub Quadruplo(Numero As Integer, Risultato As Integer)
Risultato = Numero * 4
End Sub
In questo caso, volendo assegnare alla variabile x il risultato, necessario scrivere:
Quadruplo(y,x)
Pur essendo le due soluzioni del tutto equivalenti, appare evidente la maggiore semplicit
della prima. Si noti che una funzione pu restituire un solo valore. Qualora si presentasse
la necessit di fornire al blocco chiamante pi di un'informazione, necessario ricorrere ai
parametri passati per riferimento.
Esercizio
Si provi a realizzare una funzione in grado di ricevere come parametro una stringa
alfanumerica e restituire il valore logico True se essa contiene il nome di un giorno della
settimana.

La ricorsione
Si supponga di voler realizzare una funzione in grado di calcolare il fattoriale di un numero
intero. Ricordando che il fattoriale definito solo per valori numerici positivi o nulli (in
quest'ultimo caso giova ricordare che il fattoriale di 0 1), possibile prevedere la
seguente implementazione:
Function Fattoriale(ByVal n As Integer) As Long
Dim i As Integer
Dim Risultato As Long
If n >= 0 Then
Risultato = 1
For i = 2 To n
Risultato = Risultato * i
Next i
Else

Risultato = 0
End If
Fattoriale = Risultato
End Function
Si tratta di una funzione in grado di ricevere come parametro (per valore) un dato di tipo
numerico intero e di restituire un long. L'algoritmo prevede dapprima la verifica che il
numero oggetto di elaborazione sia positivo o nullo; successivamente, esegue un ciclo che
provvede ad effettuare la serie di moltiplicazioni necessaria per il calcolo del risultato. Tale
valore infine assegnato alla funzione per fare in modo che essa lo restituisca. Si noti che
se il dato fornito in ingresso negativo, la funzione restituisce il valore 0. Ci accettabile,
non essendo possibile che il fattoriale di un numero sia nullo; si tratta quindi di un valido
indicatore della presenza di un errore.
L'algoritmo appena descritto non l'unico in grado di effettuare il calcolo del fattoriale di un
numero. Ad esempio, possibile scrivere la seguente implementazione:
Function Fattoriale(ByVal n As Integer) As Long
If n >= 0 Then
If n = 0 Then
Fattoriale = 1
Else
Fattoriale = n * Fattoriale(n - 1)
End If
End If
End Function
Il numero delle righe di codice si ridotto, a vantaggio della leggibilit. Tuttavia, a prima
vista il lettore pu rimanere sconcertato dall'uso della funzione Fattoriale all'interno della
propria definizione. Ci pu apparire come un errore. In realt, tale tecnica
perfettamente lecita e prende il nome di ricorsione.
Per rendersi conto del corretto funzionamento, si provi ad osservare ci che avviene
calcolando il fattoriale di un numero qualsiasi, ad esempio 3.
Essendo n pari a 3, la funzione esegue il calcolo
Fattoriale(3)=3*Fattoriale(2)
Ma, analogamente
Fattoriale(2)=2*Fattoriale(1)
e
Fattoriale(1)=1*Fattoriale(0)
Per mezzo di una struttura If , distinto il caso in cui n nullo. In questa condizione la
funzione restituisce il valore 1. Quindi,
Fattoriale(3)=3*(2*(1*(1)))

Ci concorda con la definizione matematica del fattoriale. L'uso della ricorsione permette
in alcuni casi di semplificare notevolmente la scrittura di una funzione e di migliorarne al
tempo stesso la leggibilit. Tuttavia, presenta numerose insidie. Si provi a valutare il
risultato prodotto dalla riga
Calcola(7)
dove la funzione Calcola definita come segue:
Function Calcola(n As Integer) As Integer
Calcola = n + Calcola(n - 1)
End Function
Il risultato costituito da un errore di sistema. Infatti, non essendo prevista alcuna
condizione di uscita, ovvero non esistendo un valore del parametro per cui restituito un
risultato non dipendente da una successiva chiamata della funzione, si genera una
successione di invocazioni di quest'ultima destinata a non avere fine, almeno sino
all'esaurimento dello spazio di memoria dedicato allo stack del sistema. Appare quindi
evidente una condizione fondamentale che deve essere soddisfatta da tutte le funzioni
ricorsive: deve sempre essere prevista una condizione di uscita.
bene tuttavia non abusare della ricorsione. Infatti, sebbene in alcuni casi semplifichi
notevolmente il compito del programmatore, talvolta pu comportare un sensibile aumento
della richiesta di risorse da parte del sistema, data la necessit che esso ha di mantenere
contemporaneamente attive pi istanze della stessa funzione.
Esercizio
Si provi a realizzare una funzione che, dato un parametro n intero positivo passato per
valore, sia in grado di restituire la somma dei primi n numeri naturali. A tal fine si faccia
uso della ricorsione.

La procedura Main
Lo studio delle subroutine termina con una procedura un po' particolare, la cui importanza
tutt'altro che trascurabile. Si tratta della procedura Main.
Un'applicazione realizzata in Visual Basic generalmente dotata di un'interfaccia utente
grafica, in cui l'interazione con gli elementi attivi determina il flusso del programma.
Tuttavia, in alcuni casi, soprattutto per la realizzazione di semplici utility in grado di
operare in modo invisibile all'utente, necessario sopprimere l'interfaccia grafica. Quando
ci avviene, il progetto risulta privo di form. Il codice deve pertanto essere posto altrove,
ovvero all'interno di moduli. Affinch una sequenza di istruzioni sia eseguita all'avvio di
un'applicazione necessario inserirla all'interno di una procedura denominata Main.
Occorre inoltre selezionare tale routine come oggetto di avvio nella finestra delle propriet
del progetto in luogo di un form. Una semplice procedura Main la seguente:
Sub Main()
If Date$ = "12-25-1998" Then
MsgBox "Buon Natale"
End If
End Sub
Si tratta di una piccola applicazione che pu essere eseguita automaticamente all'avvio di
Windows e che normalmente non fornisce alcun feedback all'utente. Solo il giorno di

Natale del 1998 essa ha un effetto visibile, in quanto provvede a visualizzare un


messaggio di auguri. Si noti l'uso della funzione standard Date$ che restituisce una stringa
contenente la data corrente. Il ricorso alla procedura Main non tuttavia riservato solo alle
applicazioni prive di form. A volte pu essere utile fare in modo che un programma
all'avvio sia in grado di valutare una condizione e in base ad essa scegliere fra i form che
lo compongono quello che deve essere visualizzato. Si supponga di disporre di una
funzione denominata SelezionaForm, di cui saranno trascurati i dettagli implementativi, in
grado di leggere il registro di sistema per determinare la lingua utilizzata dalla versione in
uso del sistema operativo e di restituire una stringa contenente una delle seguenti
sequenze alfanumeriche: "Italiano", "Inglese", "Tedesco", "Francese". Si supponga altres
di aver realizzato un progetto caratterizzato dalla presenza di 4 form, differenti fra loro per
la lingua in cui sono scritte le etichette che identificano i controlli e di aver dato ad essi
rispettivamente i nomi Ita, Ing, Ted, Fra. allora possibile scrivere una procedura Main in
grado di avviare il form corrispondente alla lingua presente nel sistema. Il codice il
seguente:
Sub Main()
Select Case SelezionaForm()
Case "Italiano":
Ita.Show vbModal
Case "Inglese":
Ing.Show vbModal
Case "Tedesco":
Ted.Show vbModal
Case "Francese"
Fra.Show vbModal
End Select
End Sub
Si notino alcune particolarit; la funzione SelezionaForm, ad esempio, non prevede
parametri. Affinch sia utilizzabile correttamente, comunque necessario richiamarla
specificando le parentesi, sebbene fra esse non sia posto alcun dato o nome di variabile.
Si noti inoltre l'uso del metodo Show, che permette di avviare il caricamento e la
successiva visualizzazione di un form. L'uso della costante predefinita vbModal accanto ad
esso stabilisce che la procedura in fase di esecuzione non pu passare all'istruzione
successiva finch il form risulta visibile. Solo dopo la sua chiusura il flusso delle istruzioni
pu riprendere e, non essendo presente altro codice, il programma pu terminare.

Conclusioni
Le procedure e le funzioni sono strumenti estremamente efficaci, in grado di fornire un
notevole aiuto al programmatore. I moderni linguaggi di programmazione orientati agli
eventi, come Visual Basic, si fondano in modo pressoch totale sulla presenza di tali
strutture.
L'acquisizione della necessaria dimestichezza nel loro uso diventa pertanto un obbligo per
colui che desidera sfruttare al meglio le potenzialit dello strumento. Per questo motivo,
ancora una volta la lezione si chiude rivolgendo al lettore un invito ad esercitarsi sugli
argomenti trattati, servendosi a tal fine anche degli esercizi proposti.