Esplorare le funzioni

Completato

Spesso ci si trova in una situazione in cui il codice appare ripetitivo. Esegue la stessa cosa in molti luoghi o ci sono solo differenze minori. In situazioni come queste, è consigliabile riflettere su come rendere riutilizzabile il codice. Il modo corretto per affrontare questa situazione consiste nell'estrarre le espressioni e creare una o più funzioni. Una funzione è un blocco predefinito fondamentale in numerosi linguaggi di programmazione ed F# non fa eccezione.

Una funzione

Una funzione è un riferimento denominato che ha un corpo costituito da una o più espressioni. Una funzione può anche avere parametri. Una funzione inizia con la parola chiave let e il nome della funzione. È quindi presente un operatore di assegnazione e, alla sua destra, il corpo della funzione.

Ecco la sintassi di una funzione:

let <function name> <parameters> = <function body>

Un esempio di effettiva funzione, mostrato nell'esempio precedente, sarà simile al seguente:

let add a b = a + b 

In questo esempio, il nome della funzione è add e i parametri sono a e b. Il corpo della funzione aggiunge i due parametri insieme,a+b, e restituisce il risultato.

Valori restituiti

Come si è osservato nel primo esempio, non è presente alcuna parola chiave return per specificare che cosa restituire. In F# le informazioni nell'ultima riga di una funzione corrispondono a quello che viene restituito. Si consideri questa funzione su più righe:

let addAndMultiply a b c = 
    let sum = a + b
    let product = sum * c
    product

Inserendo product nell'ultima riga, la funzione specifica che cosa viene restituito. Come si chiama una funzione come questa?

Chiamare una funzione

Per chiamare o richiamare una funzione, usare il nome della funzione e aggiungere tutti i parametri che si vogliono usare, separati da uno spazio. Ad esempio, per chiamare la funzione addAndMultiply(), immettere il codice seguente:

addAndMultiply 2 3 3 // 15

Per visualizzare i risultati della funzione, è possibile assegnarla a una variabile o stamparla direttamente in questo modo:

let sum = addAndMultiply 2 3 3
printfn "%i" sum
// OR
printfn "%i" (addAndMultiply 2 3 3)

Tipi dedotti

Si prenda il codice seguente:

let add a b = a + b
let sum = add 2 2 
let concat = add "hello" "world" // will give compilation error

Questo codice si interrompe all'ultima riga con il seguente messaggio:

error FS0001: This expression was expected to have type
    'int'    
but here has type
    'string'    

Il motivo dell'errore è che F# ha già determinato il tipo di parametro da usare e ha optato per Integer. Il motivo per cui ha deciso di usare Integer è dovuto alle informazioni nella seconda riga, let sum = add 2 2. La funzione add() è stata usata in un modo che indica che il tipo di parametro era sicuramente Integer.

Se la seconda riga non esiste, il codice funziona:

let add a b = a + b
// let sum = add 2 2 
let concat = add "hello" "world" // will work

Il compilatore osserva che il primo uso della funzione avviene con stringhe e conclude quindi che il tipo di parametro è una stringa. Quindi, contesto e primo utilizzo sono utili per dedurre il tipo.

Tipi espliciti

Esistono situazioni in cui si vuole essere espliciti, sia per quanto riguarda i tipi di parametri accettati che i valori restituiti dalla funzione. Un approccio più specifico consente di migliorare la leggibilità, anche se molto probabilmente il compilatore non ne ha bisogno.

Si consideri ad esempio la funzione seguente:

let convert a =
    int a

Per aggiungere tipi alla funzione, è necessario aggiungere :<type> in questo modo:

let convert (a:string) =
    int a

Il parametro ha ora il tipo string tramite il codice let convert (a:string). È ora possibile essere ancora più espliciti aggiungendo un tipo di ritorno. A questo scopo, aggiungere un altro parametro :<type> subito dopo il parametro:

let convert (a:string) :int =
    int a

Il parametro è ora racchiuso tra parentesi, come mostrato in (a:string). Il tipo restituito è l'ultima annotazione :<type> presentata, ovvero :int. F# è sufficientemente intelligente da interpretare la maggior parte degli scenari, ma a volte è necessario contribuire, rendendo più esplicito il codice. Un altro vantaggio dell'aggiunta dei tipi è che rende più semplice la comprensione del codice per un altro sviluppatore.