Funzioni Utente.

A seguito di una richiesta di un amico conosciuto sul web, provo a spiegare cosa è una "Funzione" e come sia possibile costruirsi da soli delle Funzioni, appunto le "Funzioni Utente".

In Excel abbiamo imparato cosa è una Funzione: le funzioni sono formule predefinite che eseguono calcoli utilizzando valori specifici, denominati argomenti, in un particolare ordine o struttura. Per "formule predefinite" si devono intendere una serie di istruzioni predisposte (già compilate da quei Grandi Signori che hanno programmato Excel)) che ci aiutano nella composizione delle nostre formule, evitandoci di scrivere tutta una lunga serie di istruzioni, inserendo semplicemente il nome della Funzione preposta ad un determinato calcolo. (vedi anche sul sito, sezione "formule", paragrafo "formule e funzioni - brevi cenni"). Queste Funzioni disponibili sul foglio di lavoro, hanno le corrispondenti Funzioni disponibili anche nel Vba. Ma mentre sul foglio di lavoro non è possibile creare delle Funzioni "personalizzate" , con il Vba è possibile. I vantaggi nell'impiego di una Funzione, dopo quanto detto, appaiono evidenti: si compilano delle istruzioni, a volte anche lunghe, e poi, quando serviranno, basterà richiamare il Nome della Funzione perchè le istruzioni  in essa contenute siano disponibili, senza quindi dover tutte le volte riscrivere le stesse istruzioni. Il bello è che una Funzione (compilata in un Modulo Vba), non solo sarà disponibile in una istruzione macro o in un'altra funzione, ma sarà liberamente disponibile sui fogli di lavoro, proprio come faremmo con le funzioni tradizionali di Excel. Ovviamente mi riferisco ai fogli che sono disponibili nella stessa cartella dove la funzione risiede. Una volta chiuso il file, la nostra funzione non sarà più disponibile in Excel, (è possibile ovviare anche a questo, ma lo vedremo in un altro momento), fino a quando non riapriremo la cartella che contiene la funzione.

Diversità tra Procedure e Funzioni. Semplificando si può dire che per Procedura si intende una macro (routine o subroutine) cioè una serie di istruzioni che compiono un'azione; per Funzione invece le istruzioni non agiscono, ma forniscono un valore. Questo valore è in stretta dipendenza agli  argomenti  passati alla funzione stessa. Vediamo come si scrive una funzione (sintassi):

Function [nome che assegniamo alla funzione] ( argomenti (tipo dell'argomento) ) tipo di valore in uscita

istruzioni

End Function

esempio della riga di dichiarazione funzione(tradotto in "pellegrinese"):

Function Pippo (Urca As Double) As Double

dove: Pippo = nome della funzione.

         Urca = argomento (valore di tipo numerico con decimali (As Double) da "trattare" con le istruzioni che inseriremo).

         As Double = assegnazione del tipo di dato (intero con decimali) che  Pippo restituisce.

E' sempre necessario rispettare le compatibilità tipologiche sia del dato trattato, sia del valore restituito, pena arresto del programma e proteste del compilatore. Cioè, se si dichiara che Urca è una stringa (As String) e gli si passa un numero, sarà difficile che la funzione restituisca un valore Double da una stringa (testo).

Ora, al di là delle istruzioni che inseriremo, come caspita useremo la funzione, e soprattutto, cosa si intende per argomento (quell'Urca)?. Niente di più facile: avete visto che su un foglio di Excel, se vogliamo un certo risultato in una cella (la C1 per esempio) e vogliamo usare una Funzione di Excel, scriveremo in C1 =NomeFunzione(A1) dove A1 è il riferimento alla cella su cui la funzione dovrà agire? Bene, A1 è l'argomento della funzione, o meglio il valore che si troverà in A1 (il nostro Urca). Se in C1 avessimo quindi scritto =Pippo(A1) in pratica avremmo detto ad Excel : vatti a vedere le istruzioni  contenute nella Funzione Pippo, e applicale alla cella C1 usando il valore che sta in A1. Semplice, no? Le cose non sono mai semplici, quando si fanno la prima volta, quindi proseguiamo col nostro esempio, inserendo delle istruzioni che siano illustrative. Un accorgimento: nell'assegnazione del nome alla funzione, evitiamo di usare nomi che siano già di altre funzioni tipiche di Excel. Se decidessimo di farci una Funzione personalizzata per la gestione dei decimali, evitiamo di dargli il nome ARROTONDA, perchè questo nome E' già assegnato ad una funzione di Excel. (per visualizzare tutte le funzioni presenti in Excel, dal menù "Inserisci", scegliere "inserisci funzione", e nella finestra che si apre si potranno vedere tutte le funzioni presenti, divise per categorie).

Le funzioni di Excel sono tranquillamente usufruibili nel vba (non tutte) (del come e quante sono ne parlo nel paragrafo successivo, in questa stessa sezione), quindi l'esempio che faremo sarà su una funzione che NON esiste in Vba : la funzione del foglio di lavoro =TRONCA non esiste in vba, e ci eserciteremo su quella (sto parlando di Funzione, da non confondersi con la trasposizione in Formula vba della funzione =TRONCA, regolarmente gestibile in una routine del vba).

La caratteristica della funzione =TRONCA, diversamente dalla funzione =ARROTONDA che appunto arrotonda in +/- i decimali dell'ultimo decimale richiesto, è che l'ultimo decimale viene preso così come stà, SENZA arrotondamenti. Condizione necessaria ai calcoli fiscali dove esiste una disposizione Ministeriale che precisa di "troncare" un valore al quarto decimale compreso. (faccio inoltre presente, che non è sufficiente impostare il formato celle a : numero con quattro decimali, perchè lo vedremo così, a video, ma in realtà il numero continua a possedere tutti i decimali che quindi sarebbero conteggiati in calcoli successivi.)

La sintassi della funzione TRONCA sul foglio di lavoro è:

=TRONCA(argomento1 ; argomento2)

dove: argomento1 è il riferimento alla cella che contiene il numero da troncare, argomento2 è un numero che indica a quanti decimali effettuare il troncamento. Esempio con il numero 54789,8745712 in A1 e la funzione in C1

=TRONCA(A1;4)  in C1 restituisce 54789,8745

se avessimo usato =ARROTONDA(A1;4) in C1 avremmo avuto 54789,8746

Ritornando alla nostra funzione, cercheremo di costruirla rispettando le impostazioni della versione foglio di lavoro, cioè con due argomenti in modo da poter decidere a quanti decimali effettuare il troncamento; questo per avere una funzione adattabile nel caso venissero modificate le disposizioni. Questa la funzione nella quale ho inserito anche due controlli: se la cella richiamata come primo argomento sarà vuota, si esce dalla funzione; se nella cella verrà inserito un valore non numerico, si viene avvisati con un messaggio e si esce dalla funzione:

Function Trunc(Numtot, Dex As Double) As Double

If Numtot = "" Then Exit Function   'se il valore è vuoto si esce

If IsNumeric(Numtot) = True Then  'se il valore è un numero, seguono istruzioni

num = Int(Numtot)

dec = Numtot - num

decim = Left(dec, Dex + 2)

Trunc = num + decim

Else  'altrimenti se non è un numero si avvisa e si esce

MsgBox "Il valore immesso non è un numero"

Exit Function

End If

End Function

Due spiegazioni: i nomi usati sono di fantasia: se avessimo usato Orazio e Clarabella per identificare gli argomenti, anzichè Numtot e Dex, non sarebbe cambiato niente: con Numtot si identifica il valore su cui intervengono le istruzioni, e Dex identifica il numero di decimali che vogliamo. Per prima istruzione, a parte i controlli, con num si ottiene l'intero dal valore; con dec si ottengono per differenza i decimali dal valore; con decim si prelevano a partire da sinistra tanti decimali quanti richiesti con Dex, più due (ne dobbiamo contare 2 in più perchè in questo momento i decimali (dec) ottenuti sono visti dal codice come un numero formato da uno zero, una virgola e tutti i  numeri che formano i decimali: volendo 4 decimali, in realtà ne dobbiamo prelevare (con Left) 6). A questo punto ci troviamo con un intero (num) a cui sommiamo i decimali prelevati (decim) e otteniamo il risultato voluto che viene assegnato alla funzione: infatti dichiariamo Trunc = num + decim. Se volete provarla, inserite la funzione in un modulo vba, e richiamate la funzione sul foglio di lavoro, indicando la cella di cui volete il troncamento del valore immesso, e quanti decimali volete, es.:

=TRUNC(D1;4)

Vediamo un altro esempio, questo fatto per modificare un impostazione di default nell'Arrotondamento. Sappiamo che in Excel l'arrotondamento viene fatto per difetto se il decimale da arrotondare è uguale a 4 o inferiore, in caso contrario, cioè dal 5 compreso in poi viene fatto per eccesso. Noi vorremo un impostazione diversa, che il numero che provoca l'arrotondamento per difetto non sia il 4 ma il 5, e quindi che per eccesso si consideri dal 6 compreso in pio (d'altra parte il 6 è il primo numero oltre la metà, almeno per noi comuni mortali, visto che consideriamo 1 come inizio della decina, e quindi è 5 la metà di una decina, non 4, come invece considera il computer che vede i numeri con base zero.) Al di là di questa disquisizione puramente discorsiva, ci siamo motivati la necessità di realizzare la seguente funzione, che chiameremo Arrot per non confondere con la funzione ARROTONDA, e di volere che ci restituisca una valore numerico con due decimali, tipico esempio di un mondo fatto di euro. In questo caso useremo un solo argomento vista la decisione di operare con due decimali fissi.  Vediamo un esempio ed i soliti commenti (in verde):

Function Arrot(Numtot As Double) As Double
Dim num, dec, dezero, X
num = Int(Numtot)
dec = Numtot - num
dezero = Left(dec, 4)
'estrae due decimali (scriviamo 4 per il motivo dello zero e della 'virgola spiegati nel precedente esempio)
X = Left(dec, 5)
'dobbiamo controllare se il terzo decimale è 6 o maggiore per decidere 'se arrotondare per eccesso il secondo decimale (anche qui usiamo 5 per il motivo già 'spiegato)
If Mid(X, 5) >= 6 Then
'se il terzo decimale è 6 o  maggiore, allora
dezero = dezero + 0.01
'incrementiamo di una unità il secondo decimale
Arrot = num + dezero
' e rendiamo la funzione uguale all'intero più i decimali
Else
'in caso contrario
dezero = Left(dec, 4)
'ci teniamo il secondo decimale immutato (estrae due decimali)
Arrot = num + dezero
' e rendiamo la funzione uguale all'intero più i decimali
End If
End Function

Lascio ai "pellegrini" curiosi e volenterosi il compito di munire questo secondo esempio delle protezioni necessarie ad impedire errori. Buon Lavoro.