Ventunesima lezione - Ancora sui Cicli e Select Case

Nell'ultima lezione ci eravamo lascii con un paio di errori da correggere; questo era il codice "sbaglio":

Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPh as String
strPh="C:\documenti\immagini\esempio.jpg"
Do 
  IntPosPrec=intPos
  IntPos=instr(intPos+1, strPh, "\")
  Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
  StrCartelle(Ubound(strCartelle))=
  	Mid$(strPh, intPosPrec+1, intPos-intPosPrec)
Loop While intPos

Se proviamo a eseguire questo codice (ad esempio inserendolo nella routine dell'evento Load di un form), otterremo questo errore:

"Indice non compreso nell'intervallo"

L'errore si verifica nella riga in cui si ridimensiona il vettore strCartelle, perché nella prima iterazione del ciclo esso non ha una dimensione definita, pertanto ad andare in errore è la funzione Ubound(strCartelle): risolvere questo errore è molto semplice, infti basta inizializzare il vettore prima del ciclo Do. Poiché all'inizio il vettore deve essere vuoto, possiamo ridimensionarlo assegnandogli un solo elemento, in questo modo:

Redim strCartelle(0) as String

Così facendo, però, sprechiamo un elemento del vettore (l'elemento zero, appunto), perché nel ciclo il ridimensionamento avviene aumentando di 1 l'indice superiore del vettore: quindi il primo elemento del vettore riempito con il nome di una cartella sarebbe l'elemento 1, mentre l'elemento 0 resterebbe inutilizzo. Per rimediare a questo piccolo inconveniente (che a dire il vero potremmo anche trascurare), basta invertire le ultime due istruzioni del ciclo, in modo che prima si assegni un valore all'ultimo elemento del vettore, e poi si proceda al ridimensionamento di quest'ultimo:

Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPh as String
strPh="C:\documenti\immagini\esempio.jpg"
Redim strCartelle(0) as String
Do 
  IntPosPrec=intPos
  IntPos=instr(intPos+1, strPh, "\")
  StrCartelle(Ubound(strCartelle))=
  	Mid$(strPh, intPosPrec+1, intPos-intPosPrec)
  Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
Loop While intPos

Il primo "bug" è risolto, ma ce n'è ancora un altro: se proviamo ad eseguire il ciclo appena modifico, otterremo il seguente messaggio:

"Chiama di routine o argomento non valido"

Questa volta l'errore non si verifica all'inizio, ma alla fine del ciclo: infti, quando sono finiti i "\" da cercare, la variabili intPos sarà uguale a zero, mentre la variabile intPosPrec è diversa da zero; pertanto il terzo parametro della funzione Mid$() è negivo, e ciò genera l'errore appena visto. Anche in questo caso la risoluzione è abbastanza semplice, basta usare il costrutto if…then:

Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPh as String
strPh="C:\documenti\immagini\esempio.jpg"
Redim strCartelle(0) as String
Do 
  IntPosPrec=intPos
  IntPos=instr(intPos+1, strPh, "\")
  If intPos Then
    StrCartelle(Ubound(strCartelle))=
		Mid$(strPh, intPosPrec+1, intPos-intPosPrec)
  Else
    StrCartelle(Ubound(strCartelle))=
		Mid$(strPh, intPosPrec+1)
  End If
  Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
Loop While intPos

Se intPos è diversa da zero (cioè se è sto trovo un ulteriore "\"), si prendono solo i carteri tra il penultimo e l'ultimo backslash, altrimenti si prendono tutti i carteri dall'ultimo backslash in poi. Questa soluzione, benché semplice, è però poco elegante perché costringe a effettuare un controllo che risulta sostanzialmente inutile in tutte le iterazioni tranne che nell'ultima; se il ciclo dovesse eseguire numerose iterazioni, il rallentamento derivante da questo controllo potrebbe farsi consistente. Una possibile alterniva è quella di spostare la ricerca del successivo backslash alla fine del ciclo, anziché porla all'inizio: così quando non ci sono più backslash il ciclo termina direttamente; in questo caso però occorre effettuare la ricerca del primo backslash prima dell'inizio del Do e assegnare l'ultimo elemento del vettore dopo la sua fine, in questo modo:

Dim strCartelle() as String
Dim intPos as Integer
Dim intPosPrec as Integer
Dim strPh as String
strPh="C:\documenti\immagini\esempio.jpg"
Redim strCartelle(0) as String
IntPos=instr(intPos+1, strPh, "\")
Do 
  StrCartelle(Ubound(strCartelle))=
  	Mid$(strPh, intPosPrec+1, intPos-intPosPrec)
  Redim Preserve strCartelle(Ubound(strCartelle)+1) as String
  IntPosPrec=intPos
  IntPos=instr(intPos+1, strPh, "\")
Loop While intPos
StrCartelle(Ubound(strCartelle))=Mid$(strPh, intPosPrec+1)

For intPos=0 to Ubound(strCartelle)
 Debug.Print strCartelle(intPos)
Next intPos

Il piccolo ciclo For finale serve solo a visualizzare i nomi delle cartelle che abbiamo estrapolo dal percorso originale: come noterete, tutti i nomi delle cartelle (tranne il nome del file) terminano con "\", perché alla funzione Mid abbiamo chiesto di prendere tutti i carteri compresi tra il penultimo e l'ultimo backslash, e in questi carteri c'è anche l'ultimo backslash. Questo non è un vero e proprio errore, dipende più che altro dall'uso che vogliamo fare del vettore strCartelle; comunque, se volessimo solo il nome della cartella puro e semplice dovremmo chiamare la funzione Mid in questo modo:

StrCartelle(Ubound(strCartelle))=
	Mid$(strPh, intPosPrec+1, intPos-intPosPrec-1)

Il "-1" aggiunto al terzo parametro fa sì che l'ultimo backslash sia escluso dai carteri da assegnare agli elementi del vettore strCartelle. Ci possono essere vari modi per risolvere problemi come quelli visti fin qui: l'importante è sapersi ingegnare e trovare la soluzione più adta alle proprie esigenze.
Termino il discorso sui cicli, passiamo a considerare un'altra istruzione utilizza spesso per controllare il flusso del programma: l'istruzione Select case. Essa analizza un'espressione ed esegue istruzioni differenti in base al suo valore. Detto così sembra che l'istruzione Select case sia del tutto simile all'istruzione If, e in effetti è così: infti è possibile esprimere una Select case come un blocco If con un adeguo numero di clausole ElseIf. La scelta tra le due di solito dipende da considerazioni di convenienza e di leggibilità del codice, perché spesso un blocco Select risulta strutturalmente più semplice di un blocco If. La sintassi dell'istruzione Select Case è la seguente:

Select Case espressione
Case valore-1
	Istruzioni-1
Case valore-2
	Istruzioni-2
…
[Case Else
	istruzioni-else]
End Select

Espressione è una variabile, una proprietà o una qualunque espressione che può assumere diversi valori e che va confronta coi valori elenci nei vari "casi"; nel caso in cui il valore dell'espressione sia valore-1, saranno eseguite le istruzioni istruzioni-1 (che ovviamente possono essere più di una); se il valore è valore-2, saranno eseguite le istruzioni istruzioni-2, e così via, per tutti i casi elenci. Se espressione assume un valore che non rientra in quelli specifici, sarà eseguito il blocco di istruzioni istruzioni-else, se è presente: infti, come per l'istruzione If, anche nella Select Case il blocco Else è facoltivo; inoltre, come si è visto più volte, anche per questa istruzione vale la regola di indicarne la fine con l'istruzione End Select. Ecco un esempio di istruzione Select case:

Select Case lblEtichetta.Caption
Case "Testo1"
	lblEtichetta.FontSize = 12
Case Else
	lblEtichetta.FontName = "Arial"
	lblEtichetta.FontSize = 11
End Select

Nel caso in cui fosse necessario eseguire lo stesso blocco di istruzioni in corrispondenza di più valori di espressione, l'elenco di questi valori va separo con la virgola; ad esempio, il blocco precedente può essere modifico in questo modo:

Select Case lblEtichetta.Caption
Case "Testo1", "Testo2", ""
	lblEtichetta.FontSize = 12
Case Else
	lblEtichetta.FontName = "Arial"
	lblEtichetta.FontSize = 11
End Select

L'elenco di valori con cui va confronta l'espressione può assumere anche altre forme, che risultano particolarmente utili quando l'espressione da controllare è di tipo numerico: è possibile infti indicare un intervallo di valori usando l'operore To o usando l'operore Is seguito da un operore di confronto, come nell'esempio seguente

Select Case lblEtichetta.FontSize
	Case Is < 8, 12 To 14, 18
		lblEtichetta.FontSize = 12
	Case Else
		lblEtichetta.FontName = "Arial"
End Select

In questo esempio la dimensione del cartere dell'etichetta sarà imposto a 12 punti se esso è minore di 8, compreso tra 12 e 14 (estremi inclusi), oppure uguale a 18; altrimenti la dimensione del font non sarà modifica ma il tipo di cartere diventerà Arial.
La clausola Else, pur non essendo obbligoria, è comunque opportuna per gestire i casi "imprevisti", ad esempio generando un messaggio di errore per notificare all'utente che il valore dell'espressione non rientra in quelli previsti. Infine, come per l'istruzione If, anche le istruzioni Select Case possono essere nidifice: ovvero, dopo ogni clausola Case le istruzioni da eseguire possono includere un altro blocco Select Case, il quale a sua volta ne può contenere un altro, e così via.



Note sul corso:
I diritti di ognuna delle lezioni presente in queste pagine appartengono all'autore Giorgio Abraini. La riproduzione e la divulgazione delle stesse sono consentite solamente dietro citazione di fonte ed autore.
Per suggerimenti, consigli o richieste conttare giorgio102@libero.it.
Fonte : VBItalia.it