SOMMARIO
Funzioni Excel standard e personali in ambiente VBA ....................................................................................................1 Premessa: tre opportune distinzioni .............................................1 Funzioni Excel fruibili nel codice VBA e VB....................................2
La libreria di oggetti di Excel..........................................................................................2 Uso delle funzioni Excel in VBA e in Visual Basic standard.............................................4
In questa trattazione ci occuperemo in qualche dettaglio dei tre casi, ma allo scopo di evitare (facili) confusioni, conviene offrire subito esempi relativi a ciascuno dei tre casi predetti: Caso a:
y = Application.Vlookup(x, Range(TabSconti), 2) z = Application.Vlookup(x, MiaMatrSconti, 2)
Premesso che Application.Vlookup il corrispettivo, in VBA, della funzione Excel CERCA.VERT(Cosa;Tabella;Indice), per la quale si rimanda alla Guida chi non la conoscesse, supponiamo poi che lintervallo denominato, sul foglio di lavoro, TabSconti contenga una tabella di sconti del tipo seguente: Importo 0 10.000 40.000 100.000 Sconto% 0,00% 2,50% 5,00% 7,50%
e che gli stessi dati siano stoccati nella matrice VBA MiaMatrSconti, di 3 righe e 4 colonne. Ebbene non sar arduo verificare che, se putacaso x = 15.000, entrambe le istruzioni restituiscono 0,025. Caso b:
ActiveCell.FormulaR1C1 = "=VLOOKUP(RC[-1],TabSconti,2)"
Supponendo, per fissare le idee, che sia attiva la cella C6, accanto alla C5 contenente un dato importo, il precedente codice inserisce in C6 la formula seguente: =CERCA.VERT(C5;TabSconti;2) Caso c:
y = MiaFunz(x)
F10=MiaFunz(F9) Si suppone che sia stata creata una certa funzione dutente MiaFunz, diciamo nel Modulo1:
Function MiaFunzione(x As Double) As Double . . . opportune istruzioni . . . End Function
Ebbene, i due precedenti esempi illustrano (genericamente) altrettante possibilit duso delle MiaFunz, in una routine VBA o nella cella F10 del foglio.
Si spalancher un elenco di tutte e sole le funzioni supportate. Per brevit, citiamone solo alcune, a titolo di saggio, con accanto lequivalente sul foglio di lavoro delledizione italiana:
Funzione VBA Funzione sul foglio Cos Average ChiTest Choose Count Dcount Dsum Hlookup GammaDist Lookup Rank Round Sum Vlookup COS MEDIA TEST.CHI SCEGLI CONTA DB.CONTA DB.SOMMA CERCA.ORIZZ DISTRIB.GAMMA CERCA RANGO ARROTONDA SOMMA CERCA.VERT
Significato Coseno Media aritmetica Test per lindipendenza Scegli da un elenco Conteggio Conteggio su un database Somma su un database Ricerca tabellare orizzontale Distribuzione Gamma Ricerca gabellare (semplice) Posizione di un dato in un elenco Arrotondamento Somma Ricerca gabellare verticale
Sono abbastanza immediate le osservazioni riportate qui di seguito, che comunque non guastano.
WorksheetFunction pleonastico, serve pi che altro a filtrare le funzioni in parola dal mare magnum delle propriet e metodi delloggetto Application. Questultimo invece obbligatorio. In altri termini, Application.WorksheetFunction.Sum e Application.Sum sono equivalenti (ma, ovviamente, Sum, da solo, errato!). La sintassi inglese dellambiente VBA si estende alle funzioni Excel, pure nelledizione italiana (e non si dimentichi che il separatore degli argomenti la virgola, non il punto e virgola). Il punto (.), utilizzato in diverse funzioni sul foglio di lavoro, sparisce in ambito VBA (ovvio: qui il punto funge da basilare separatore di oggetti, propriet e metodi!). Mancano allappello funzioni assai particolari (come tutte le ingegneristiche) nonch le funzioni di trattamento testi e quelle di date e orari. Delle ultime due non si sente la mancanza, visto che esistono gi nel Visual Basic standard.
NOTA Vi sono alcuni duplicati (da evitare). Cos Application.Trim (corrispettivo dellitaliana ANNULLA.SPAZI() equivale alla Trim del VB standard. C poi una strana (e inutile!) Application.Asc(A) che restituisce A e non il codice Ascii 65 come la Asc(A). Puntualizziamo ora una regola molto importante, gi anticipata nel paragrafo dapertura. Regola. Le funzioni del foglio di lavoro si applicano, in ambiente VBA, sia agli intervalli sia alle matrici Visual Basic. E questo indipendentemente dal fatto che glindici di matrice partano da 0 (direttiva default Option Base 0) o da 1 (Option Base 1).
Invece in Visual Basic standard, ipotizzando un progetto in cui il calcolo della media ricorre pi volte, si dovr creare una funzione ad hoc, evocabile come nella successiva routine di prova:
Function MiaMedia(Matr) For i = 0 To UBound(Matr) Tot = Tot + Matr(i) Next MiaMedia = Tot / i End Function
Sub ProvaMiaMedia() Dim MiaMatr(4) MioVett = Array(1, 2, 3, 4, 5) For i = 0 To 4 MiaMatr(i) = MioVett(i) Next MsgBox MiaMedia(MiaMatr) End Sub
Il Debugger protesta segnalando un conflitto tipologico sulla chiave Ubound(Matr) applicata al vettore MioVett passato alla funzione MiaMedia. Probabilmente molti lettori sono gi incappati in questa inattesa quanto frustrante situazione e magari i pi esperti, oltre che dio, sanno perch. Chi scrive no, e si stupisce del fatto che Ubound(MioVett) sia invece accettato nei casi in cui non si invoca una routine. Ma a che servono le discussioni? Occorre rassegnarsi e prendere atto che non si pu passare impunemente una variante contenente un Array come argomento di una funzione o di una Sub. Ma la cosa che pi di tutte va sottolineata che le funzioni di Excel possono essere fruite non solo nel VBA di Excel, ma altres:
in ogni altro ambiente VBA, nel VBA di Word, di Access ecc.; in VB standard, vale a dire in un qualsiasi programma Visual Basic.
Ricordiamo la manovra da compiere, nellEditor Visual Basic: 1. dal menu Strumenti scegliere Riferimenti 2. nella susseguente finestra, attivare la Microsoft Excel 9 Object Library. Se le precedenti operazioni sono stata compiute, vale la seguente Regola - Le funzioni di Excel si possono richiamare Excel.WorksheetFunction. o, semplicemente, Excel.. anteponendo
NOTA - Si osservi che il richiamo diretto, tramite Excel. pur essendo un meccanismo di OLE Automation, fa a meno delle complicazioni sintattiche del tipo CreateObject & affini. Vi sono per, e non vanno dimenticate, delle controindicazioni: a) la libreria di Excel piuttosto pesante; b) le funzioni create direttamente in VB sono pi veloci; c) nella macchina dellutente deve, ovviamente, essere installato Excel. Per fortuna i PC odierni sono sempre pi veloci e dotati di memorie molto ampie, inoltre difficile che non sia presente Excel. Conclusione: A chi desidera sviluppare applicazioni sofisticate e non ha tempo da perdere (e, lo si confessi: non ha la competenza necessaria) per implementare in proprio certe sofisticate funzioni della cucina Excel conviene passare per questo ricco convento.
10 11 12 20 21
...........
Opportune, e scontate, formule in colonna D e seguenti calcolano un incremento dello 0,5% rispetto al mese precedente (dio lo volesse!, ma un esempietto didattico). Si supponga poi che allintervallo D11:F11 sia stato preassegnato il frivolo nome Formuline. Se, come sovente accade, il numero di voci non dato a priori, diversi utenti (specie se provenienti da Lotus 1-2-3) affidano il compito di copiare dinamicamente le formule in basso a una macro come la seguente:
Sub CopiaDinamica() With Range("Formuline") r = .Cells(1, 0).End(xlDown).Row - .Row + 1 c = .Cells.Columns.Count .Copy Destination:= Range(.Cells(1, 1), .Cells(r, c)) End With End Sub
La precedente routine, determina la riga r, in senso relativo allintervallo Formuline tramite lultima cella della colonna C, a sua volta individuata mediante la propriet End(xlDown) (v. Guida o anche il Manualino introduttivo del qui presente autore, gi pubblicato in questo stesso sito Microsoft) e la colonna, sempre relativa, c tramite Columns.Count. Infine si sfrutta il metodo Copy che, con argomento Destination, funziona a distanza e senza selezionare alcunch. NOTA - Non si vorrebbe polemizzare, ma ci capita troppo spesso che utenti mediamente esperti o persino sedicenti guru VBA usino macro strapiene di metodi Select, a volte con salti continui anche da foglio a foglio, che quasi sempre si possono tranquillamente evitare! Il procedimento appena visto valido, ma a nostro avviso lo ancor pi il seguente (lasciato alla commento e alla sperimentazione autogestita):
Sub InserimentoDinamico() With Range("Iniform") r = .Cells(1, 0).End(xlDown).Row - .Row + 1 c = .Cells(0, 1).End(xlToRight).Column - .Column + 1 Range(.Cells(1, 1), .Cells(r, c)).FormulaR1C1 = "=RC[-1]*1.05" End With End Sub
NOTA - Luso di FormulaR1C1 oltre che pi elegante, ha il pregio di inserire soltanto formule, mentre il metodo Copy copia pure i formati, il che spesso complica le cose.
Osserviamo poi che i simboli $ che contrassegnano i riferimenti assoluti ($A$1) o misti ($A1 e A$11) non contano in molte istruzioni VBA, in quanto Range("B1:F20"), Range("$B$1:$F$20") o Range("$B1:$F20") individuano il medesimo intervallo. Ma nella copia e nell'inserimento di formule con Ctrl+Invio i dollari tornano ad avere... corso legale. Se C1:C10 la selezione corrente, un conto scrivere in C1, prima di copiarla o consolidarla nella selezione, =$A$1*B1, un conto dimenticarsi i dollari e limitarsi a =A1*B1. Nel primo caso in C2, C3, C4... troveremmo le formule =$A$1*B2, =$A$1*B3, =$A$1*B4..., nel secondo si avrebbero le formule =A2*B2, =A3*B3, =A4*B4..., di ben diverso significato (e risultato disastroso). Ma perch adottare lo stile R1C1 in luogo del pi semplice A1? Perch il secondo meno potente e flessibile del primo, come stiamo per mostrare. Per comprendere la cosa, si pensi che la selezione attuale sia C10:C50, a che C10 sia la cella attiva e di voler inserire in tale intervallo, la formula =C9+1 atta a creare una serie progressiva di valore iniziale (variabile) posto in C9. Listruzione seguente pu soddisfare tale requisito:
Selection.Formula = "=C9+1"
La pratica, e qualche riflessione, svela per uninadeguata flessibilit di tale codice. Infatti spesso Selection sottintende la possibilit di generalizzarlo a qualsiasi intervallo e non solo a quello che inizia in C10. Peggio ancora vanno le cose se si vuol passare un oggetto Range come argomento di una routine. Con la zona predetta va tutto bene: in C10 si ha =C9+1, in C11=C10+1 eccetera, per con una diversa selezione, putacaso E5:E11, la stessa istruzione ci dar di nuovo C9+1 in E5, C10+1 in E6, e cos via, una serie identica all'altra, mentre ne avremmo voluto una che dipendesse dal valore nella cella sovrastante E5, ossia E4. Con lo stile R1C1 si avrebbe invece:
Selection.FormulaR1C1 = "=R[-1]C"
In tal modo si evita linconveniente appena lamentato perch i riferimenti R[-1]C puntano a una riga sopra, stessa colonna. NOTA - Si provi con Selection.Formula = "=" & Selection.Cells(1, 1).Offset(-1, 0)Address(False, False) & "+ 1". Si raggiunge lo scopo, ma al prezzo di concatenamenti astuti ma laboriosi (specie con formule meno stupide!). Per chiudere coi riferimenti R1C1 riportiamone la casistica sintattica nella tabella che segue.
Stile
RnCm RnC RCm Rn Cm RC
Significato
Riga n, colonna m Riga n, stessa colonna Stessa riga , colonna m Riga n Colonna m
Equivalente A1 $E$3 A$5 $D1 $5 $F Non esiste Non esiste Non esiste Non esiste
Stessa riga e colonna (rif. RC circolare) Scarto di n righe, stessa R[-2]C - R[3]C colonna Stessa riga, scarto di m R[2]C - R[-1]C colonne Scarto di colonne n righe e m R[2]C[-2]
R[n]C RC[m]
R[n]C[m]
R1C1:R[9] C
=SOMMA($A$1:A10)
Si fa notare che talune equivalenze sono imperfette, data la superiore flessibilit della notazione R1C1. Una particolarit per i pi abili e curiosi. La distinzione tra le propriet FormulaR1C1 Formula e Value solo formale e si pu constatare, ad esempio, la piena equivalenza fra I codici seguenti:
Formula = "=A1+A22" e FormulaR1C1 = "=A1+A2" FormulaR1C1 = "=R[-2]C+R[-1]C" e Formula = "=R[-2]C+R[-1]C" e, persino: Value = "=R[-2]C+R[-1]C"
Linserimento di formule pu addirittura far a meno tanto di Formula che di FormulaR1C1. Si provi con Range("B12") = "=RAND()" o con Cells(3, 4) = "=R[1]C+1". Funzionano entrambe, in quanto Excel tramuta in formula ogni stringa che inizia con =.
Per fortuna, con Excel 97 e 2000, modelli con macro italiane vengono tradotte automaticamente al caricamento. Ma la scelta Microsoft pienamente giustificata. Tra laltro: in questo mondo sempre pi globalizzato solo cos un modello creato in Italia pu essere fruito da un corrispondente in qualsiasi altro paese. La conseguenza, per il discorso che stiamo facendo, che, per essere il VBA rigorosamente e pienamente anglofono, una formula come la seguente errata: Range(A1).FormulaR1C1 = "=SOMMA(R1C1:R10C1) Si deve, invece, scrivere: Range(A1).FormulaR1C1 = "=SUM(R1C1:R10C1) Ma niente paura: la precedente sintassi scrive poi nella cella di Excel italiano la formula =SOMMA($A$1:$A$10) (e formule analoghe, in francese o tedesco ecc., vengono inserite a beneficio di colleghi stranieri, in possesso della propria edizione nazionale). Da questa quasi perfetta virt poliglotta (*) emerge per la difficolt derivante dalle sintassi inglesi delle funzioni Excel? Si e no. NOTA (*) Le poche eccezioni hanno a che fare con stringhe italiane relative a talune finestre di dialogo. Ad esempio Selection.Font.FontStyle = "Grassetto". Non cos con Selection.Font.Bold = True, che pertanto caldamente consigliato!.
2. immettere in una cella qualsiasi la funzione desiderata, digitandola in italiano, ad esempio =MEDIA(A1:A10); 3. interrompere il Registratore, poi portarsi nellEditor Visual Basic e spulciare la Macro1 nel Modulo1 da esso creata. Si trover qualcosa come:
ActiveCell.FormulaR1C1 = "=SUM(R[-10]C:R[-1]C)"
E il gioco fatto, si dir. S, ma che scomodit. E il piacere di avere sottomano una tabella completa, dove lo mettiamo? Purtroppo si tenterebbe invano di reperire tale tabella nella Guida. Per ovviare allinconveniente, a beneficio anzitutto di se stesso, ma ora anche degli utenti pi esperti che usano (o intendono usare prima o poi) funzioni anche avanzate da inserire tramite macrocodice il qui presente autore si costruito da solo tale tabella, comprendente quasi tutte le funzioni italiane di Excel con, a fianco le equivalenti inglesi e descrizioni sintetiche sul significato di ciascuna. Per farla breve, essa reperibile nel file seguente: FunzioniItaliane_inglesi.xls Nel quale sono riportate, nel primo foglio, entrambe le funzioni e relative spiegazioni, mentre gli altri tre riportano solo le italiane, le inglesi e le spiegazioni.
TraduttoreFunzioni.xls FunzioniItaliane_Inglesi.csv
Il secondo in formato CSV (Comma Separated Values) e, come avverte la macro davvio del primo occorre assolutamente che si trovi nella medesima directory di TraduttoreFunzioni.xls. Lo scopo di questo primo traduttore subito visto. Basta caricarlo in Excel e fare clic sul pulsante Traduttore o sulla WordArt. Compare linequivocabile finestra di dialogo seguente:
Se per si fa clic sul pulsante Copia Appunti lutente avvertito che questa funzionalit non la si potuta implementare in VBA, per il semplice motivo che in tale ambiente latita loggetto ClipBoard e relative propriet quali SetText e GetText che consentono di utilizzare gli Appunti. A quel punto chi scrive, preso il coraggio a quattro mani, ha realizzato in Visual Basic 6 lequivalente utility .EXE. Corrisponde ai seguenti 4 file, anche qui tutti tassativamente da collocare in una medesima cartella di file:
I vari archivi .txt sono testi puri separati, di ovvio significato. Se non altro, permettono a chi vuole di correggere eventuali errori e/o di modificare le spiegazioni a suo gusto. In questo caso possibile utilizzare il piccolo Traduttore direttamente nellEditor Visual Basic di Excel:
Ecco le manovre da compiere: 1. lanciare lutility; 2. portarsi nellEditor VBA digitando il codice tipo FormulaR1C1 ecc., poi di nuovo attivare lutility (con Alt+Tab), in modo da sovrapporla allEditor; 3. selezionare la funzione italiana desiderata; 4. dare un clic sul pulsante Copia Appunti; 5. nellEditor premere Ctrl+V incollando la funzione inglese nel punto dinserzione corrente. A questo punto ci manca lo spazio per commentare le routine contenute nella cartella di lavoro TraduttoreFunzioni.xls, i cui algoritmi sono suppergi simili a quelli adottati nellutility TraduttFunzioniExcel.exe. Oltretutto sono abbastanza avanzate. Comunque riportiamo qui di seguito una macro cruciale, con scarni commenti a beneficio dei pi esperti.
Private Sub UserForm_Initialize() Dim MioRec As String, Dato As String, i As Integer, n As Integer p = ThisWorkbook.Path & "\" ListBox1.Clear 'Forse qui superfluo... Open p & "FunzioniItaliane_Inglesi.csv" For Input As #1 i = 0 While Not EOF(1) Line Input #1, MioRec pone successivamente in MioRec le righe del file .csv n = InStr(1, MioRec, ";") Dato = Left(MioRec, n - 1) ListBox1.AddItem Dato MioRec = Replace(MioRec, Dato & ";", "", 1, 1) ReDim Preserve FunzionIngl_Spiegaz(i) FunzionIngl_Spiegaz(i) = MioRec i = i + 1 Wend Close #1 ListBox1.ListIndex = 0 End Sub
La precedente Sub, che agisce al caricamento della UserForm, esordisce ponendo in p il percorso completo in cui si trova ThisWorkbook, ossia TraduttoreFunzioni.xls. Tale percorso dato dalla propriet Path, al quale va concatena la barra inversa \.
In tal modo la successiva Open in gradi di aprire il file .csv a condizione che esso si trovi nella medesima directory del file .xls. Il successivo ciclo While Wend provvede, per ciascun record del file .csv, a isolarne i tre campi (separati dal ;"), ponendo il primo nella lista delle funzioni italiane (ListBox1) e gli altri due nei vettori FunzionIngl_Spiegaz(i) e FunzionIngl_Spiegaz(i). Questi vettori, il cui nome spiega tutto, sono visibili in tutta la UserForm (essendo definiti a livello modulo), vengono utilizzati dalle altre macro devento di questultima per far corrispondere a ciascuna funzione italiana la corrispondente italiana e la relativa spiegazione sintetica.
Totali Alterni
La figura che segue pone in drammatica evidenza come possa nascere, in un caso della vita, lesigenza della prima funzione proposta.
In casi del genere gli utenti si arrangiano con formule di addizione, del tipo seguente: L6 =D6+F6+H6+J6 M6=E6+G6+I6+K6 Ma questa soluzione poco soddisfacente per due motivi: molto tediosa con Intervalli estesi ed sensibile allinserzione di celle. Di qui lidea di creare una funzione dutente capace di comportarsi come la ben nota SOMMA(), ma totalizzando solo le celle dispari. un uovo di Colombo (cui per pochi pensano!) sicch la riproduciamo qui sotto senza commenti:
Function SommaAlterna(VettoreIntervallo As Range) Dim i As Integer With VettoreIntervallo For i = 1 To .Count Step 2 SommaAlterna = SommaAlterna + .Cells(i) Next End With End Function
Due sole osservazioni: a) la predetta funzione, che somma le celle dispari, va bene sia in colonna L che in colonna M (v. figura precedente), e grazie allaggiustamento automatico dei riferimenti pu essere copiata da L in M o inserita con Ctrl+Invio in L6:M22 in un sol colpo; b) Se proprio occorre ciascuno pu crearsi una funzione che somma le celle pari, basta far girare i a partire da 2, sempre con Step 2.
Somme diagonali
Queste somme diagonali nascono da uno sfizio del qui presente autore, che si voluto cimentare coi quadrati magici realizzati su un foglio Excel. NOTA - I quadrati magici di cui si parla contengono numeri da 1 a n^2 disposti in modo che le somme di tutte le righe, colonne e delle due diagonali sono uguali.
Comunque queste somme lungo le diagonali di un intervallo quadrato possono servire altrove, non si sa mai. Le riportiamo qui sotto, senza commentarle, salvo ricordare una regola importante (e non a tutti nota). Regola - Alle celle degli oggetti Range si pu accedere tramite la propriet Cells o, pi semplicemente, tramite indici. Sicch ad es. Range(A1:C5).Cells(1, 2) equivale a Range(A1:C5)(1, 2) (che pi elegante).
Function SommaDiagDisc(ZonaQuadrata As Range) Dim NumRig As Integer NumRig = ZonaQuadrata.Rows.Count For i = 1 To NumRig SommaDiagDisc = SommaDiagDisc + ZonaQuadrata(i, i) Next End Function Function SommaDiagAsc(ZonaQuadrata As Range) Dim NumRig As Integer NumRig = ZonaQuadrata.Rows.Count k = NumRig For i = 1 To NumRig SommaDiagAsc = SommaDiagAsc + ZonaQuadrata(i, k) k = k - 1 Next End Function
Coloro poi non disdegnano siffatte frivolezze possono dare unocchiata al foglio di lavoro Somme diagonali esplorandone le varie formule. E ad essi sono dedicate due funzioni pi sofistiche, ossia:
SommaDiagonale(ZonaQuadrata As Range, Discendente As Boolean) una funzione ibrida delle precedenti due; EstQuadratoMagico(Quad As Range), che restituisce VERO o FALSO a seconda se Quad magico o meno.
Entrambe visibili nel Modulo1 della cartella .xls di cui stiamo parlando.
Media Ponderata
Nella figura che segue mostrata la parte bassa del solito modello. Vi si illustra come pu nascere lesigenza del calcolo di una media ponderata. Sulla sinistra, esso viene svolto mediante formule normali, sulla destra con la funzione dutente MediaPond().
Neanche qui dovrebbero necessitare commenti, salvo forse ribadire laccesso indicizzato a un oggetto Range (ad es. ZonaPesi(i) equivale - ma pi bello a ZonaPesi.Cell(i).