CISA FotoGallery

Statistiche

Tot. visite contenuti : 914042
Home Articoli tecnici HowTo Le Matrici

Le Matrici

Una matrice (array) è una variabile che può contenere valori multipli. Le matrici sono utili quando è necessario memorizzare un numero di valori dello stesso tipo, ma di cui non si conosce la quantità, o non si voglia creare distinte variabili per memorizzarle tutte.
Per esempio, supponiamo di memorizzare un valore numerico per ogni giorno dell'anno.
Si potrebbero dichiarare 365 variabili numeriche separate, ma ciò sarebbe un enorme lavoro. Invece si può creare una matrice per memorizzare tutti i dati in una variabile.
La matrice stessa è una singola variabile con elementi multipli; ciascun elemento può contenere una parte dei dati.


Si possono usare cicli, assieme ad una coppia di funzioni speciali per lavorare con le matrici, per assegnare valori o ottenere valori dai vari elementi di una matrice.

Creare le matrici

Si possono creare due tipi di matrici in VBA - matrici a grandezza fissa e dinamiche.
Una matrice a grandezza fissa ha un numero fisso di elementi, ed è utile soltanto quando si conosce esattamente la quantità degli elementi che potrebbe avere la matrice mentre si sta scrivendo il codice. Il più delle volte si creeranno matrici dinamiche.

Le matrici possono essere di qualunque tipo di dati. Il tipo dati di una matrice, specifica il tipo di dati di ciascun elemento di una matrice: per esempio, ciascun elemento di una matrice di tipo Long può contenere un valore Long.
Il seguente frammento di codice dichiara una variabile di tipo Long

 


Dim alngNum() as Long

Nota Non è necessario includere le parentesi quando si fa riferimento ad una variabile matrice, salvo quando si dichiara, la si ridimensiona, o ci si riferisce a un distinto elemento. Comunque si può includere la parentesi ogni qual volta si voglia chiarire che quella variabile è una matrice.

Una volta che si è dichiarato una variabile matrice dinamica, si può ridimensionare la matrice utilizzando la dichiarazione ReDim.
Per ridimensionare una matrice, si deve fornire un valore per il limite superiore e, in opzione, per il limite inferiore. I limiti superiori ed inferiori di una matrice si riferiscono agli indici iniziali e finali della matrice.

E' d'obbligo specificare il limite superiore della matrice quando la si ridimensiona. Il limite inferiore è opzionale, ma è buona norma includerlo, così diventa evidente quale sia il limite inferiore:

 

' Questa matrice contiene 100 elementi
ReDim alngNum(0 To 99)


Se non si include il limite inferiore, esso è determinato dell'impostazione dell'Option Base per il modulo.
Per default, l'impostazione dell'Option Base per un modulo è 0. Si può impostare ad 1 inserendo Option Base 1 nella sezione delle Dichiarazioni del modulo.


Se si sta utilizzando la dichiarazione ReDim su una matrice che contiene valori, questi valori possono essere perduti quando la matrice è ridimensionata. Per essere sicuri che ogni valore nella matrice sia mantenuto, si può utilizzare la parola chiave Preserve con la dichiarazione ReDim, come segue:

 

ReDim Preserve alngNum(0 To 364)
  

Il ridimensionamento di una matrice con la parola chiave Preserve può esser lento, così non conviene farlo tanto frequentemente. Un modo migliore per minimizzare l'uso della parola chiave Preserve nel codice è stimare il quantitativo di dati necessario da memorizzare e dimensionare di conseguenza la matrice. Se avviene un errore perchè non si è dimensionata la matrice in maniera sufficiente, la si può ridimensionare tutte le volte che necessita all'interno dell'handler di errore. Una volta che si lavora con la matrice, se essa è più grande del necessario, si può ridimensionare quel tanto che contenga i dati correnti. L'esempio, più avanti, "Ottenere una Matrice da una Funzione" dimostra questa tecnica.

Matrici e Variant

Una variabile di tipo Variant può memorizzare una matrice. Per esempio, il seguente frammento di codice assegna una matrice di tipo String ad una variabile di tipo Variant
 


Dim astrItems(0 To 9) As String
Dim varItems as Variant
varItems = astrItems



Quando una matrice di tipo statico è inizializzata, o quando una matrice di tipo dinamico è ridimensionata, ciascun elemento è inizializzato in accordo al suo tipo. In altre parole, gli elementi di tipo String sono inizializzati a stringhe di lunghezza-zero, gli elementi di tipo Integer e Long sono inizializzati a zero (0), e gli elementi di tipo Variant sono inizializzati a Empty. Il punto è che nell'esempio precedente, non è necessario riempire la matrice per lavorarci. Semplicemente dichiarando una matrice di dieci elementi come tipo String, si crea una matrice di dieci stringhe a lunghezza-zero.

Una matrice di tipo Variant può memorizzare qualunque tipo di dati in ognuno dei suoi elementi.Per esempio una matrice di tipo Variant può avere un elemento di tipo String, un elemento di tipo Long, ed un altro di tipo Date. Può persino memorizzare una variabile Variant che contiene un'altra matrice.

Una matrice di tipo Variant può anche memorizzare una matrice di oggetti. Se si conosce che una matrice memorizzerà solo oggetti, si può dichiarare la matrice di tipo oggetto.

Suggerimento Si può considerare l'utilizzo di un oggetto Collection o Dictionary per memorizzare gruppi di oggetti in una singola variabile, piuttosto che creare una matrice di oggetti. Il vantaggio ad utilizzare una matrice su un oggetto Collection o Dictionary è quello della semplicità nell'ordinarlo. Ma se si memorizzano oggetti, probabilmente non importa l'ordinamento. Dal momento che un oggetto Collection o Dictionary si ridimensiona automaticamente, non c'è da preoccuparsi nel mantenere la traccia della sua dimensione, come invece avviene con una matrice.


Assegnare una Matrice ad un'altra

Se due matrici dinamiche hanno lo stesso tipo di dati, si può assegnare una matrice ad un'altra. Assegnare una matrice ad un'altra dello stesso tipo è veloce perchè la prima matrice è semplicemente puntata alla locazione di memoria che memorizza la seconda matrice. Questa caratteristica è nuova in questa versione di VBA (Access 2000); non è consentita nelle precedenti versioni.

Per esempio, il seguente frammento di codice assegna una matrice stringa ad un'altra:


Dim astr1() As String
Dim astr2(0 To 9) As String
astr1 = astr2



Importante Questo tipo di assegnazione lavora solo per le matrici dinamiche dello stesso tipo. Le due matrici debbono necessariamente essere matrici dinamiche entrambe, ed esse debbono essere dichiarate dello stesso tipo: se una è di tipo String, l'altra deve essere di tipo String. Non possono essere di tipo Variant o di qualunque altro tipo dati. Se si vogliono assegnare elementi di una matrice ad una matrice di tipo differente, si deve creare un ciclo ed assegnare ciascun elemento uno alla volta.


Ottenere una Matrice da una Funzione

Il precedente esempio assegna una matrice di tipo variabile ad un'altra. Basandosi su quest'esempio, si potrebbe supporre che si possa anche richiamare una procedura che restituisce una matrice ed assegnarla ad un'altra matrice, come nel seguente frammento di codice


Dim astr1() As String
astr1 = ReturnArray



Per restituire una matrice, una procedura deve avere un tipo di valore di ritorno del tipo dati della matrice, o di tipo Variant. Il vantaggio di dichiarare una procedura per restituire una matrice scritta verso un valore Variant è che non si ha bisogno di utilizzare la funzione IsArray per assicurarsi che invece la procedura restituisce una matrice. Se una procedura restituisce un valore di tipo Variant, ci sarebbe bisogno di controllare i suoi contenuti prima di eseguire le operazioni sulla matrice.

La procedura ReturnArray suggerisce all'utente l'input e crea una matrice dei valori risultanti, ridimensionandola come necessario. Notare che per restituire una matrice da una procedura, si assegna semplicemente la matrice al nome della procedura.


Function ReturnArray() As String()
' Questa funzione riempie una matrice con l'input dell'utente, poi
' restituisce la matrice.

Dim astrItems() As String
Dim strInput As String
Dim strMsg As String
Dim lngIndex As Long

On Error GoTo ReturnArray_Err

strMsg = "Enter a value or press Cancel to end:"

lngIndex = 0

' Invita l'utente al inserire il primo elemento da aggiungere alla matrice.
strInput = InputBox(strMsg)
If Len(strInput) > 0 Then
' Stima la dimensione della matrice.
ReDim astrItems(0 To 2)
astrItems(lngIndex) = strInput
lngIndex = lngIndex + 1
Else
' Se l'utente annulla senza aggiungere l'elemento,
' non ridimensiona la matrice.
ReturnArray = astrItems
GoTo ReturnArray_End
End If

' Invita l'utente per elementi addizionali da aggiungere alla matrice.
Do
strInput = InputBox(strMsg)
If Len(strInput) > 0 Then
astrItems(lngIndex) = strInput
lngIndex = lngIndex + 1
End If
' Ciclo fintanto che l'utente annulla.
Loop Until Len(strInput) = 0

' Ridimensiona al valore corrente di lngIndex - 1.
ReDim Preserve astrItems(0 To lngIndex - 1)

ReturnArray = astrItems

ReturnArray_End:
Exit Function

ReturnArray_Err:
' Se si va oltre il limite superiore, ingrandisce la matrice.
If Err = ERR_SUBSCRIPT Then ' Subscript out of range.
' Raddoppia la dimensione della matrice.
ReDim Preserve astrItems(lngIndex * 2)
Resume
Else
MsgBox "E' avvenuto un errore inaspettato!", vbExclamation
Resume ReturnArray_End
End If
End Function


Quando si richiama una procedura che restituisce una matrice, è necessario metter in conto il caso in cui la matrice restituita non contenga alcun elemento. Per esempio, nella precedente procedura ReturnArray, se si annulla la inputbox la prima volta che compare, la matrice restituita dalla procedura non contiene alcun elemento. La procedura della chiamata ha bisogno di controllare questa condizione. Il miglior modo per farlo è definire una procedura come quella che segue che prende una matrice e controlla il limite superiore. Se la matrice non contiene alcun elemento, il controllo del limite superiore causa un errore intercettabile.


Function IsArrayEmpty(varArray As Variant) As Boolean
' Determina se una matrice contenga qualche elemento.
' Restituisce False se contiene elementi, True
' se non li contiene.

Dim lngUBound As Long

On Error Resume Next
' Se la matrice è vuota, ne consegue un errore, quando
' si controllano i limiti della matrice.
lngUBound = UBound(varArray)
If Err.Number <> 0 Then
IsArrayEmpty = True
Else
IsArrayEmpty = False
End If
End Function


Nota Le funzioni Split e Filter di VBA possono anche restituire una matrice che non contenga alcun elemento. Controllando i limite inferiore o superiore di una matrice restituite da una delle due procedure, comunque non ne consegue un errore. Quando la funzione Split o Filter restituisce una matrice che non contiene alcun elemento, il limite inferiore di quella matrice è 0, e quello superiore è -1. Perciò, per determinare se una matrice contiene qualche elemento, si può controllare la condizione che il limite superiore sia minore di quello inferiore.


Passare una Matrice ad una Procedura

Si può dichiarare una matrice in una procedura, e poi passare la matrice ad un'altra procedura per essere modificata. La procedura che modifica la matrice non necessariamente ha bisogno di restituire una matrice. Le matrici sono passate come riferimento, cioè una procedura passa all'altra un puntatore della locazione di memoria della matrice. Quando la seconda procedura modifica la matrice, essa la modifica nella stessa locazione di memoria. Perciò, quando l'esecuzione restituisce la prima procedura, la variabile matrice si riferisce alla matrice modificata.

Ordinare le Matrici

Ordinare una matrice è un processo iterativo che richiede un bel sofisticato agoritmo. Un esempio di un comune algoritmo di ordinamento è il seguente:


Function QuickSortArray(varArray As Variant, _
Optional lngFirst As Long = -1, _
Optional lngLast As Long = -1) As Variant

' QuickSort algoritmo utilizzato per ordinare gli elementi
' nella matrice varArray.

Dim lngLow As Long
Dim lngHigh As Long
Dim lngMiddle As Long
Dim varTempVal As Variant
Dim varTestVal As Variant

If lngFirst = -1 Then lngFirst = LBound(varArray)
If lngLast = -1 Then lngLast = UBound(varArray)

If lngFirst < lngLast Then
lngMiddle = (lngFirst + lngLast) / 2
varTestVal = varArray(lngMiddle)
lngLow = lngFirst
lngHigh = lngLast
Do
Do While varArray(lngLow) < varTestVal
lngLow = lngLow + 1
Loop
Do While varArray(lngHigh) > varTestVal
lngHigh = lngHigh - 1
Loop
If (lngLow <= lngHigh) Then
varTempVal = varArray(lngLow)
varArray(lngLow) = varArray(lngHigh)
varArray(lngHigh) = varTempVal
lngLow = lngLow + 1
lngHigh = lngHigh - 1
End If
Loop While (lngLow <= lngHigh)
If lngFirst < lngHigh Then QuickSortArray varArray, lngFirst, lngHigh
If lngLow < lngLast Then QuickSortArray varArray, lngLow, lngLast
End If
End Function


La sub per effettuare il test:


Sub TestQuickSort(lngNum As Long)
' Tests the QuickSortArray procedure with an array of random integers.

Dim alngNums() As Long
Dim lngI As Long

' Ridimensiona la matrice per ottenere gli elementi lngNum.
ReDim alngNums(0 To lngNum - 1)

' Sviluppa un generatore di numero causale.
Randomize
' Riempie la matrice con valori interi casuali.
For lngI = 0 To lngNum - 1
alngNums(lngI) = Int(Rnd * 100)
Next

' Ordina la matrice.
QuickSortArray alngNums

' Mostra nella finestra Imediata gli elementi della matrice.
PrintArray alngNums
End Sub


Una piena spiegazione dell'algoritmo va fuori lo scopo di questo articolo.
In breve, l'algoritmo QuickSort funziona utilizzando una strategia del tipo dividi-e-ordina. Esso primo trova l'elemento mediano della matrice, poi lavora dall'elemento più a destra a quello di mezzo, e poi dall'elemento più a sinistra a quello di mezzo, confrontando gli elementi su entrambi i lati del valore di mezzo e scambiando i loro valori se necessario. Una volta che questa parte di ordinamento è completo, i valori sul lato destro sono più grandi di quelli del lato sinistro, ma non necessariamente in ordine. La procedura allora osserva i valori sul lato sinistro utilizzando la stessa strategia - trovando un valore centrale e scambiando gli elementi su entrambi i lati. Fa questo fino a quando i valori sl lato sinistro sono ordinati, poi passa al lato destro. La procedura richiama se stessa in modo ricorsivo fino a quando l'intera matrice non è stata ordinata.


Ricerca in una Matrice

Quando è necessario determinare se una voce esiste in una matrice, come si fa? Si può ciclare attraverso la matrice, controllando ciascun elemento, fintanto si trova quello giusto. Comunque, questo metodo, benché ovvio, non è efficace. La successiva sezione mostra dei modi alternativi alla ricerca, in una matrice.

Utilizzare la Funzione Filter per cercare Matrici di Stringa

La funzione Filter rende più facile la ricerca di una stringa se è necessario conoscere semplicemente l'esistenza di un elemento in una matrice. La funzione Filter prende una matrice di stringa e una stringa contenente il testo da ricercare. Essa restituisce una matrice ad una-dimensione contenente tutti gli elementi che corrispondono al testo ricercato.

Uno svantaggio potenziale dell'uso della funzione Filter nella ricerca in una matrice è quello di non restituire l'indice degli elementi della matrice che corrispondono al testo ricercato. In altre parole, la funzione Filter ci dice se un elemento esiste in una matrice, ma non dove.

Un altro potenziale problema utilizzando la funzione Filter per la ricerca in una matrice è che non vi è alcun modo di specificare se il testo ricercato corrisponda all'intero elemento, o se a parte di esso. Per esempio, se si utilizza la funzione Filter per la ricerca di un elemento che corrisponda alla lettera "e," la funzione Filter restituisce non solo quegli elementi che contengono solo la "e," ma anche ogni elemento che ne contenga uno più grande come "e."

La seguente procedura incrementa le potenzialità della funzione Filter per la ricerca in una matrice e restituisce solo gli elementi che corrispondono. La procedura FilterExactMatch prende due argomenti: una matrice stringa per la ricerca e una stringa per trovarla. Essa utilizza la funzione Filter per restituire una matrice contenente tutti gli elementi che corrispondono alla stringa di ricerca, sia parzialmente o per intero. Essa poi controlla ciascun elemento nella matrice filtrata per verificare che corrisponda esattamente alla stringa di ricerca. Se gli elementi corrispondono esattamente, essa è copiata ad una terza matrice di stringa. La funzione restituisce questa terza matrice, che contiene le esatte corrispondenze.


Function FilterExactMatch(astrItems() As String, _
strSearch As String) As String()

' Questa funzione ricerca una matrice di stringa per gli elementi
' che esattamente corrispondono alla stringa di ricerca.

Dim astrFilter() As String
Dim astrTemp() As String
Dim lngUpper As Long
Dim lngLower As Long
Dim lngIndex As Long
Dim lngCount As Long

' Filtra la matrice per la stringa di ricerca.
astrFilter = Filter(astrItems, strSearch)

' Memorizza i limiti superiori ed inferiori della matrice risultante.
lngUpper = UBound(astrFilter)
lngLower = LBound(astrFilter)

' Ridimensiona la matrice temporanea per essere delle stesse dimensioni.
ReDim astrTemp(lngLower To lngUpper)

' Cicla attraverso ciasun elemento nella matrice filtrata.
For lngIndex = lngLower To lngUpper
' Controlla l'esatta corrispondenza alla stringa di ricerca.
If astrFilter(lngIndex) = strSearch Then
' Memorizza gli elementi che corrispondono esattamente nell'altra matrice.
astrTemp(lngCount) = strSearch
lngCount = lngCount + 1
End If
Next lngIndex

' Ridimensiona la matrice contenente le esatte corrispondenze.
ReDim Preserve astrTemp(lngLower To lngCount - 1)

' Restituisce la matrice contenente le esatte corrispondenze.
FilterExactMatch = astrTemp
End Function


La sub per effettuare il test:


Sub TestFilterExactMatch(strText As String, _
strSearch As String)

' Test la funzione FilterExactMatch.

Dim astrTest() As String
Dim astrResult() As String

astrTest() = Split(strText)

astrResult = FilterExactMatch(astrTest, strSearch)
PrintArray astrResult
End Sub


Utilizzare una Funzione di Ricerca di tipo Binario per la Ricerca in Matrici Numeriche

La funzione Filter lavora bene per la ricerca in matrici di stringhe, ma non è efficace per le matrici numeriche. Per utilizzare la funzione Filter per una matrice numerica, si debbono convertire tutti i numeri in stringhe, un passo in più che abbassa la prestazione. Poi si debbono eseguire le operazioni di confronto-stringhe, quando i confronti di tipo numerico sono molto più veloci.

Sebbene sia più coinvolto, l'algoritmo di ricerca-binario esegue una efficace ricerca su una matrice ordinata - sia numerica che stringa. L'algoritmo ricerca-binaria divide una set di valori a metà, e determina se il valore ricercato è nella prima o seconda metà. Qualsiasi metà contenente il valore è mantenuta, e l'altra metà scartata. La rimanente metà è nuovamente divisa a metà e il processo si ripete fino a quando o si arriva al valore ricercato, o si determina che non si trova nel set. Notare che la matrice deve essere ordinata affinchè questo algoritmo lavori.
 

Function BinarySearch(varArray As Variant, _
varSearch As Variant) As Variant

' Questa funzione cerca una matrice per un elemento e restituisce
' l'indice del primo elemento corrispondente, se se ne trova uno.

Dim lngUpper As Long
Dim lngLower As Long
Dim lngMid As Long

' Controlla se la variant è una matrice.
If IsArray(varArray) Then
' Se è così, la ordina.
QuickSortArray varArray

' Memorizza limiti superiore e inferiore.
lngUpper = UBound(varArray)
lngLower = LBound(varArray)

' Esegue il ciclo fino a quando i limiti superiore ed inferiore
' non sono uguali.
Do While lngLower < lngUpper
' Trova l'elemento di mezzo.
lngMid = (lngUpper + lngLower) \ 2
' Determina se l'elemento ricercato ricade
' all'interno di questo intervallo.
' Ridimensiona i limi inferiore e superiore.
If varSearch > varArray(lngMid) Then
lngLower = lngMid + 1
Else
lngUpper = lngMid
End If
Loop
' Ritorna l'indice dell'elemento se trovato.
' Altrimenti restituisce -1.
If varArray(lngLower) = varSearch Then
BinarySearch = lngLower
Else
BinarySearch = -1
End If
Else
' Restituisce Null se una matrice non è passata.
BinarySearch = Null
End If
End Function


La sub per effettuare il test:


Sub TestBinarySearch()
' Testa la funzione BinarySearch.

Dim lngIndex As Long

Dim avarTest(0 To 9) As Long

For lngIndex = 0 To 9
avarTest(lngIndex) = lngIndex
Next

Debug.Print BinarySearch(avarTest, 6)
End Sub


Tratto dal Cap 7° della Microsoft Office 2000/Visual Basic Programmer's Guide: "Getting the Most Out of Visual Basic for Applications"