Corso diVisual Basic(Parte 7)di Maurizio CrespiContinua lo studio dei vettori; questo mese sono protagonisti quelli a più dimensioni, le strutture caratterizzate dal poter variare dinamicamente il numero degli elementi che le compongono e gli array di controlli Nella scorsa lezione è stata introdotta la nozione di vettore, o array. Per mezzo di questa struttura di dati, è possibile associare un unico nome a un insieme di posizioni di memoria dello stesso tipo, ognuna delle quali può contenere un dato diverso. In questa settima puntata del corso dedicato alla programmazione in Visual Basic sarà esteso il concetto e ne saranno descritte le evoluzioni, ovvero saranno studiati i vettori caratterizzati dal possedere più di una dimensione e gli utilissimi array di controlli, che talvolta contribuiscono notevolmente a semplificare il compito del programmatore.
Le soluzioni degli esercizi proposti nella precedente lezioneIl primo passo, prima di procedere allo studio dei nuovi argomenti, prevede come sempre la verifica della comprensione di quelli esposti nel numero precedente, mediante la correzione degli esercizi in esso proposti. Primo esercizio Il primo esercizio richiede la modifica dell'applicazione, descritta nello scorsa puntata, in grado di leggere 100 numeri e di elencarli in ordine crescente all'interno di una casella di testo. È infatti richiesto di far sì che effettui l'ordinamento in modo decrescente. La soluzione è banale, in quanto richiede semplicemente l'inversione dell'operatore di confronto all'interno del ciclo che provvede all'ordinamento. Il codice necessario per ordinare un vettore contenente 100 valori numerici in modo decrescente per mezzo dell'algoritmo di selezione diretta è pertanto quello indicato di seguito: For i = 1 To 99 For j = i + 1 To 100 If Valori(i) < Valori(j) Then Temp = Valori(i) Valori(i) = Valori(j) Valori(j) = Temp End If Next j, i Secondo esercizio Il secondo esercizio presenta una complessità leggermente superiore. Esso richiede infatti di realizzare un'applicazione in grado di estrarre a sorte 30 numeri, di calcolarne la media aritmetica e di visualizzare all'interno di una label solo quelli che risultano inferiori ad essa. Per la sua realizzazione, l'applicazione richiede la creazione di un form, in cui va posta un'etichetta testuale denominata lblRisultato. Il codice che provvede al suo riempimento deve essere eseguito all'avvio. Può pertanto essere associato all'evento Load del form. La procedura, che è possibile osservare nel Listato 1, è costituita da 3 cicli. Il primo ha lo scopo di estrarre a sorte 30 valori interi compresi fra 1 e 100. Di essi è calcolata la media aritmetica per mezzo della seconda struttura di iterazione. Tutti i valori che risultano inferiori a questo numero sono elencati, uno sotto l'altro, all'interno della casella di testo. Ciò avviene per mezzo del terzo ciclo.
Vettori con indice minimo diverso da 0 o 1Si noti che la dichiarazione del vettore risulta leggermente diversa da quelle viste nella scorsa lezione. In questo caso, infatti, è stato specificato anche l'indice minimo, secondo la sintassi Dim <nome> (<indice_minimo> To <indice_massimo>) As <tipo> Ciò consente di creare dei vettori in cui gli indici possono essere anche negativi, oppure appartenere a un campo di valori molto diverso da quello usuale. Ad esempio, supponendo di voler creare un array in grado di ospitare un dato numerico indicante il prodotto interno lordo dello Stato italiano negli anni compresi fra il 1989 ad oggi, è possibile dichiarare la variabile PIL come segue: Dim PIL(1989 To 1998) As Double Naturalmente, il vettore è del tutto equivalente a uno di 10 elementi dichiarato con il metodo usuale, però l'uso di indici strettamente correlati alle informazioni contenute nella struttura offre il vantaggio di migliorare la leggibilità del codice.
I vettori a dimensione variabileSi supponga di voler scrivere un'applicazione in grado di leggere un dato numerico corrispondente alla temperatura massima per ogni giorno di un mese, di calcolare la media di tutti i valori e di indicare in quali giorni la temperatura si è mantenuta al di sotto del valore medio calcolato. In pratica, l'applicazione risulta estremamente simile a quella descritta in precedenza. In questo caso, tuttavia, i valori non sono casuali, bensì inseriti dall'utente. Inoltre, il loro numero non è fisso, in quanto dipendente dal mese a cui si riferiscono i dati. Una possibile soluzione è quella indicata nel Listato 2. ReDim [Preserve] <nome> (<dimensione>) Il suo scopo è di variare la dimensione del vettore di cui è specificato il nome. L'istruzione può essere ripetuta all'interno del programma per un numero di volte teoricamente infinito. La procedura descritta nel Listato 2 può essere resa più efficiente mediante l'uso di un vettore dinamico, come indicato nel Listato 3. Si noti che è cambiata la dichiarazione del vettore, che nella nuova procedura costituita dalla riga Dim V() As Integer La dimensione questa volta non è stata specificata; il compito di allocare la giusta quantità di memoria per le informazioni è demandato alla riga ReDim V(NumGiorni) che provvede a dimensionare il vettore in modo che il massimo indice sia pari al valore della variabile intera NumGiorni, il cui valore è fornito dall'utente.
La clausola PreserveSi supponga ora di voler modificare la procedura sopra descritta per fare in modo che non richieda all'avvio il numero dei valori da elaborare, bensì acquisisca ciclicamente dei dati fino alla digitazione da parte dell'utente della stringa "Fine". In questo caso, non è possibile conoscere la dimensione del vettore prima dell'inserimento dei dati. Esso quindi deve essere in grado di crescere continuamente. Il codice necessario per svolgere il compito richiesto è descritto nel Listato 4. Si noti la presenza di un ciclo delimitato dalle parole chiave Do e Loop. Esso risulta privo delle clausole While o Until. L'uscita è infatti causata dall'istruzione Exit For, che è eseguita quando l'utente digita la parola "Fine". Osservando il codice, è possibile constatare che la stringa inserita dall'utente è convertita in maiuscolo per mezzo della funzione UCase ed è privata degli eventuali spazi iniziali e finali per mezzo della funzione Trim. In tal modo, il confronto risulta indipendente dall'uso delle lettere maiuscole. L'istruzione ReDim è invocata ad ogni iterazione, al fine di incrementare la dimensione del vettore di un'unità per fare posto al dato appena letto. Dovendo intervenire su una struttura già contenente dei dati e volendo fare in modo che essi non siano persi, occorre utilizzare l'istruzione ReDim con la clausola Preserve; in sua assenza, il ridimensionamento dell'array comporterebbe la cancellazione di tutti i valori contenuti. All'interno della procedura si è fatto uso anche della funzione UBound, che ha lo scopo di calcolare il massimo indice previsto dal vettore, ovvero la sua dimensione. Esercizio Si provi a realizzare un'applicazione in grado di simulare lo spoglio delle schede elettorali. Essa deve leggere per ogni scheda il numero del candidato votato. Alla fine, deve essere in grado di indicare la classifica in ordine decrescente.
Vettori a due indiciSi supponga ora di voler modificare il programma per fare in modo che sia in grado di effettuare le proprie valutazioni facendo riferimento sia alle temperature massime, sia alle minime e che indichi i giorni in cui almeno uno dei due valori è risultato al di sotto della media. In questo caso, quindi, per ogni giorno è necessario acquisire due valori. Una soluzione consiste nell'utilizzare due vettori, uno dedicato alle temperature massime e l'altro alle minime. Il codice è descritto nel Listato 5. Esso non costituisce un esempio di compattezza ed efficienza. Infatti, le istruzioni utilizzate per gestire le temperature minime sono praticamente duplicate per gestire le massime. La lunghezza del codice può essere ridotta facendo ricorso a una matrice costituita da due colonne, di cui una è dedicata alle temperature minime e l'altra alle massime. La procedura così modificata è descritta nel Listato 6. Il primo indice determina la colonna della matrice. Il valore 1 fa riferimento alle temperature minime, mentre il valore 2 si riferisce alle massime. Il secondo, ridimensionabile per mezzo dell'istruzione ReDim, è invece utilizzato per far riferimento al giorno. Si osservi che l'uso dell'istruzione ReDim non è possibile sul primo indice. Infatti, solo l'indice posto più a destra nella dichiarazione di una matrice può essere ridimensionato. L'introduzione di una seconda dimensione comporta una semplificazione del codice. Per operazioni più complesse, è possibile incrementare ulteriormente il numero delle dimensioni di un vettore, purché rimanga inferiore a 60. Nella procedura appena descritta, si è fatto nuovamente uso della funzione UBound, che in questo caso richiede un ulteriore parametro indicante la posizione della dimensione di cui si desidera conoscere la grandezza. I vettori di controlliSi supponga ora di voler modificare l'applicazione per fare in modo che sia in grado di richiedere su un unico form i valori delle temperature minime e massime rilevate in tutti i giorni di una settimana. L'interfaccia deve prevedere l'acquisizione dei dati che avviene per mezzo di caselle di testo. Si tratta di creare un form e di aggiungere le textbox necessarie, che si supporranno denominate LunediMin, LunediMax, MartediMin, MartediMax, ecc. Occorre poi modificare la procedura precedentemente descritta sostituendo la parte dedicata all'inserimento dei dati nella matrice con il seguente codice: V(1,1)=Val(LunediMin.Text) V(2,1)=Val(LunediMax.Text) V(1,2)=Val(MartediMin.Text) V(2,2)=Val(MartediMax.Text) V(1,3)=Val(MercolediMin.Text) V(2,3)=Val(MercolediMax.Text) V(1,4)=Val(GiovediMin.Text) V(2,4)=Val(GiovediMax.Text) V(1,5)=Val(VenerdiMin.Text) V(2,5)=Val(VenerdiMax.Text) V(1,6)=Val(SabatoMin.Text) V(2,6)=Val(SabatoMax.Text) V(1,7)=Val(DomenicaMin.Text) V(2,7)=Val(DomenicaMax.Text) La sequenza di istruzioni presentata non rappresenta un esempio di semplicità. Nel caso in cui le caselle di testo, anziché essere 14, fossero in un numero sensibilmente maggiore, il programma potrebbe diventare poco leggibile. Si osservi che di fatto le righe sono estremamente simili fra loro. Ciò che varia è il nome della textbox. Grazie alle caratteristiche di Visual Basic, è possibile operare una notevole semplificazione del codice utilizzando un vettore di controlli, nella fattispecie rappresentati da caselle di testo. Un array di questo tipo non richiede alcuna dichiarazione. È sufficiente far uso della proprietà Index dei controlli. Ad esempio, si supponga di voler realizzare il form in grado di acquisire tutte le temperature rilevate nell'arco della settimana. Dopo aver agito sul menu Progetto ed aver selezionato la voce Inserisci form, si provvede ad inserire sul modulo creato una casella di testo e ad assegnarle il nome txtMinima. Ad essa possono successivamente essere assegnati i valori desiderati delle proprietà che definiscono il carattere, il colore e la dimensione. Si crea così l'elemento campione, che può essere duplicato per mezzo di una normale operazione di copia e incolla. Si noti che, nel momento in cui si incolla il primo elemento, appare un messaggio che segnala la presenza sul form di un altro oggetto denominato txtMinima e presenta una finestra di dialogo che ha lo scopo di chiedere al programmatore se desidera creare un array. Rispondendo Si, si crea un vettore di caselle di testo denominato txtMinima, in cui gli elementi che lo compongono sono caratterizzati dal possedere un diverso valore della variabile Index. Quest'ultima costituisce l'indice che identifica in modo univoco l'elemento nel vettore. È tuttavia possibile realizzare una matrice anche riunendo dei controlli aventi dei nomi diversi, purché siano dello stesso tipo. In questo caso, occorre inserire tutti gli elementi nel form e successivamente associare ad essi dei diversi valori della proprietà Index. Dopo aver fatto ciò, è possibile variare il contenuto delle proprietà Name ed assegnare a tutti la stessa stringa. Tornando all'esempio, si supponga di creare 7 copie dell'oggetto txtMinima. Si supponga altresì di agire in modo analogo con un'altra casella di testo, denominata txtMassima. In tal modo, si creano 2 vettori, destinati a contenere rispettivamente le temperature minime e massime rilevate. Grazie ad essi, il codice necessario per riempire il vettore V si riduce a poche righe: For i=1 to 7 V(1,i)=val(txtMinima(i).Text) V(2,i)=val(txtMassima(i).Text) Next i Si noti la notevole compattezza che lo caratterizza e la possibilità di adattarlo alla gestione di un numero maggiore di textbox semplicemente variando il valore massimo della variabile di controllo del ciclo. L'accesso alle proprietà e ai metodi di ogni elemento del vettore richiede obbligatoriamente l'indicazione dell'indice fra parentesi tonde. Un'interessante caratteristica dei vettori di controlli consiste nell'utilizzo della stessa procedura per rispondere agli eventi generati da ogni singolo elemento. Ad esempio, si supponga di disporre di un form dotato di 3 pulsanti, etichettati Primo, Secondo, Terzo, che costituiscono un vettore denominato btnBottoni. Volendo generare un messaggio alla pressione di un pulsante che indichi quale di essi è stato premuto, è possibile scrivere la seguente procedura: Private Sub btnBottoni_Click(Index As Integer) MsgBox btnBottoni(Index).Caption End Sub Essa è associata all'evento Click di tutti gli elementi del vettore. Si noti che fra parentesi è specificata la variabile Index. Essa ha valore solo all'interno della procedura ed identifica l'indice dell'elemento che ha generato l'evento. Per mezzo della funzione MsgBox, in grado di far apparire sullo schermo una finestra contenente un messaggio, è possibile visualizzare l'etichetta testuale del pulsante premuto, facendo riferimento alla proprietà Caption dell'elemento di indice corrispondente al valore della variabile Index. La procedura descritta risulta adatta a gestire vettori di dimensione qualsiasi. Ancora una volta appare evidente la caratteristica fondamentale degli array: lo stesso codice può gestire allo stesso modo e con la stessa facilità delle strutture sia di piccole, sia di grandi dimensioni. Esercizio Si provi a realizzare un'applicazione che preveda una pulsantiera costituita da tasti numerati da 1 a 9. La pressione di uno di essi deve provocare l'incremento di un contatore numerico di un'entità pari all'indicazione presente sulla sua etichetta. Il valore del contatore deve essere visualizzato per mezzo di una label.
ConclusioniL'uso dei vettori permette di aumentare notevolmente la flessibilità e il livello di astrazione del proprio codice. Per mezzo di quelli a più dimensioni e agli array di controlli, diventa spesso possibile ridurre notevolmente la quantità di codice necessaria per realizzare un'applicazione, a tutto vantaggio della leggibilità. Data l'importanza degli argomenti proposti, l'invito rivolto al lettore è come sempre di esercitarsi su di essi, al fine di raggiungere un buon livello di comprensione. (c) 1998 Edizioni Infomedia srl |