Capire le funzioni, cosa sono e come funzionano

Questo è il post #8 di 12 nella serie “Fondamenti di Programmazione 101

Una funzione è un modo per dare un nome a un blocco di codice e, attraverso questo nome, il blocco di codice può essere invocato ripetutamente ogni volta che vogliamo eseguirlo.

Decidere cosa inseriamo all’interno di una funzione

Lo scopo principale di una funzione è quella d’incapsulare il nostro codice in porzioni di codici più piccole e isolate. Un altra funzione molto importante è quella di poter riutilizzare tali porzioni in posti diversi senza dover duplicare codice e come conseguenza, si migliora la manutenibilità in quanto, qualunque cambiamento necessario potrà essere fatto soltanto all’interno della funzione evitando il rischio di effettuare la modifica in un posto magari dimenticandosi di modificare altri posti dove il codice risulta duplicato. Questa tecnica è conosciuta, in ambito dello sviluppo del software, come il “DRY principle” (acronimo per Don’t Repeat Yourself).

Quando dobbiamo decidere cosa una funzione debba contenere, c’è solo una regola da seguire. Una funzione dovrebbe sempre fare una sola cosa (conosciuto come il Single Responsibility Principle) e il nome dovrebbe riflettere l’intento della funzione.

Come egregiamente espresso da Robert C. Martin (amichevolmente conosciuto come Uncle Bob)

A function should do something or answer something, but not both.

e un altra citazione di Uncle Bob è la seguente:

The first rule of functions is that they should be small. The second rule is that they should be even smaller.

Queste due citazioni sollevano un interessante quesito però, supponiamo di avere un task ben identificato e quindi inseriamo la sua logica all’interno di una singola funzione, rispettando peraltro il primo quote di Uncle Bob ma la funzione diventa piuttosto grande (diciamo qualche centinaio di righe di codice).

Questo violerebbe la seconda citazione? Decisamente si e quindi dobbiamo risolvere questa situazione.

Una soluzione sarebbe quella di analizzare la nostra funzione e trovare dei sotto tasks che possono essere estratti in funzioni separate.

La domanda che dovreste avere, a questo punto è la seguente: Se creiamo una funzione rispettando la prima citazione di Uncle Bob in quanto fa una sola cosa, spezzare il codice in altre funzioni violerebbe la seconda citazione? Non necessariamente!

English Breakfast - Yummi!
Facciamo un esempio della vita reale, pensiamo a quando prepariamo la colazione (io amo l’English Breakfast :D).

Il processo di preparazione della colazione è un task specifico e quindi potrebbe andare all’interno di una singola funzione ma inoltre, può essere spezzato in sotto processi che poi messi insieme costituiranno la nostra colazione.

English Breakfast sub-functions

Scrivere una funzione

Vediamo come scrivere una funzione in pseudo codice:

Definizione di una funzione

Dopo il nome della funzione, abbiamo una coppia di parentesi tonde aperte e chiuse. Ogni qualvolta invochiamo una funzione, dobbiamo includerle dopo il nome della stessa.

Una funzione può essere invocata semplicemente attraverso il suo nome e non dobbiamo dimenticarci d’includere le parentesi. Chiamare la precedente funzione risulterà nella seguente dichiarazione: name()

Ritornare valori da una funzione

L’idea dietro le funzioni non è soltanto quella d’incapsulare blocchi di codici ma anche quella di fare qualcosa che produce un valore.

All’interno di una funzione, quando una dichiarazione contiene la parola chiave return la funzione termina a prescindere se sono presenti ulteriori dichiarazioni dopo il return. Inoltre la funzione ritornerà, al chiamante della funzione, qualunque valore definito dopo il return.

Quindi, una funzione non soltanto viene utilizzata per incapsulare della logica ed eseguirla ogni volta che ne abbiamo la necessità ma inoltre possiamo ottenere dati che la funzione ritorna.

Vediamo un esempio di come possiamo utilizzare una funzione e il valore ritornato:

Funzione - Valore  di Ritorno

Il precedente snippet di codice stamperà la string Ciao, Mondo!

Argomenti delle funzioni

Spesso, non vogliamo che la nostra funzione faccia delle cose in maniera statica ma vogliamo renderla più flessibile in modo da cambiare il suo comportano sulla base di valori esterni.

Continuiamo sulla base del nostro ultimo esempio. Supponiamo di voler creare una funzione che saluti il mondo e un altra che saluti una specifica persona. Nel primo caso riutilizziamo la funzione definita precedentemente e nel secondo caso usiamo la seguente funzione:

Esempio di funzione

Il problema che abbiamo adesso dovrebbe essere subito evidente. Se avessimo 10 possibili saluti diversi, dovremmo scrivere 10 diverse funzioni il cui codice sarebbe esattamente uguale a parte la stringa che segue Ciao,.

A questo punto dobbiamo trovare un modo per rendere la nostra funzione più flessibile in modo che possa ritornare un valore diverso sulla base di un input esterno.

Possiamo ottenere ciò passando dei valori a una funzione, questi valori sono chiamati i parametri della funzione. Possiamo passare zero o più parametri ad una funzione definendoli all’interno della coppia di parentesi che seguono il nome della funzione. Questi parametri sono delle variabili, che possiamo usare all’interno della funzione e che conterranno il valore che abbiamo passato dal chiamante della funzione.

Facciamo il refactoring della nostra funzione avvalendoci del concetto di parametro.

Funzione generica per salutare

L’esecuzione del precedente snippet di codice produrrà il seguente risultato:

Ciao, Mondo Ciao, Marco

Ricapitoliamo

In questo articolo abbiamo visto che le funzioni hanno un nome e quel nome è usato per invocare il metodo. Quando invochiamo una funzione, il codice all’interno della funziona viene eseguito.

Una funzione può ritornare un risultato dato dall’esecuzione del codice all’interno della funzione. Successivamente possiamo utilizzare il risultato della funzione nel codice restante nel nostro programma.

Abbiamo anche visto come una funzione può prendere zero o più parametri che possono essere utilizzati come variabili locali nel codice all’interno della funzione.

Per concludere, è importante notare che il concetto di funzione introduce l’idea che le variabili possono essere globali o locali. Le variabili globali possono essere accedute da qualunque posto all’interno del nostro programma e le variabili locali possono essere utilizzate solo all’interno della funzione dove la variabile locale è stata creata.