domenica 15 gennaio 2012

F# - Tipi derivati

Come è stato detto negli articoli precedenti F# possiede una serie di tipi nativi del linguaggio, come il tipo intero, float, boolean e così via. Ma come è stato detto, questi sono dei tipi nativi, già presenti nel linguaggio, che possono, però, contribuire alla creazione di nuovi tipi, i così detti tipi derivati. Esistono, sostanzialmente, tre categorie di tipi derivati: tipi enumerati, tipi unione e tipi record.


Tipi enumerati

Nella categoria dei tipi enumerati rientrano i tipi creati mediante l'enumerazione degli elementi che fanno parte dell'insieme del tipo. Se per esempio vogliamo creare un nuovo tipo, che rappresenti l'insieme dei giorni della settimana. Quello che dobbiamo fare è dare un nome ad ogni giorno della settimana e indicare che quel elemento appartiene all'insieme dei giorni della settimana. Di seguito è stato rappresentato l'insieme del tipo che andremo a creare:

giorno = {Lunedì, Martedì, Mercoledì, Giovedì, Venerdì, Sabato, Domenica}

In altre parole, l'insieme appena citato contiene soltanto i valori elencati. La creazione di un tipo enumerato in F# viene fatto in un modo analogo:

type giorno= Lunedi | Martedi | Mercoledi | Giovedi | Venerdi | Sabato | Domenica;;

In questo modo abbiamo definito un nuovo tipo giorno, il cui insieme dei valori è uguale a quello elencato in precedenza. Proviamo ad eseguire in F# Interactive(Ctrl+Alt+F) il codice di seguito riportato:

let x = Sabato;;

Come risposta dell'interprete avremo questo messaggio:

val x : giorno = Sabato

Da questa risposta possiamo vedere che l'interprete ha deciso automaticamente che la variabile x è di tipo giorno ed il suo valore è uguale a Sabato
A questo punto potremo scrivere una funzione, che prende come argomento un giorno della settimana e come risultato restituisce un valore booleano, per indicare se il giorno è lavorativo:

let lavorativo day = 
    match day with
    | Sabato | Domenica -> false
    | _ -> true;;

La funzione, quindi, prende come argomento un valore di tipo giorno e lo confronta con il valore Sabato e Domenica. Se day è uguale a Sabato, oppure a Domenica, allora restituisce false, perché day non rappresenta un giorno lavorativo. In tutti gli altri casi day rappresenta un giorno lavorativo, quindi la funzione restituisce true.


Tipi unione

A differenza dei tipi enumerati i tipi unione non devono essere elencati uno ad uno, quando si vuole definire un nuovo tipo, perché essi vengono creati sulla base degli altri tipi, già definiti. In pratica per definire un nuovo tipo unione dobbiamo elencare soltanto i suoi elementi e il tipo di tali elementi. Per esempio, potremo creare un tipo, che permetta di identificare un oggetto dal nome, oppure da un numero identificativo. Di seguito è riportato il codice di come sia possibile farlo:

type id = Name of string | Id of int;;

In questo modo verrà definito il tipo id, che ha come elementi Name e Id. Ma anche se la definizione del tipo unione è molto simile a quella dei tipi enumerati, l'impiego di questi tipi è leggermente diverso. Di seguito è riportato un esempio di come sia possibile dichiarare una variabile di tipo id:

let identity = Name "Tizio";;
let identity2 = Id 4231;;

Come si può vedere dall'esempio, bisogna fare attenzione a scrivere prima l'elemento dell'insieme del tipo, seguito dal valore del rispettivo tipo. Il vantaggio di avere questo modo di definire i tipi è quello di poter utilizzare il tipo stesso nella sua definizione, che ci permette di creare delle strutture informatiche molto importanti, quali le liste concatenate e gli alberi. Di seguito è riportato il codice che ci permette di creare una lista concatenata, una struttura dati molto simile alle liste:

type MyList = Empty | Nodo of int * MyList;;

In questo modo una variabile di tipo MyList può assumere il valore Empty, che rappresenta la lista vuota, oppure un valore formato da una coppia da un valore di tipo int e uno di tipo MyList. Concatenando in questo modo più valori è possibile creare una lista lunga a piacere di elementi. Ecco un esempio di come si può creare una lista di questo tipo:

let lista = Node(1,Node(2,Node(3,Empty)));;

La particolarità di questo tipo di struttura è che possiamo aggiungere una quantità illimitata di elementi e in ogni momento abbiamo la possibilità di aggiungerne un'altro, cosa molto utile nel caso in cui non sappiamo quanti elementi dobbiamo esaminare. 


Tipi record

I tipi record differiscono dagli altri due per il fatto che è più adatto a descrivere le caratteristiche di un determinato oggetto o soggetto, per esempio l'eta, l'altezza ed il peso di una persona, per cui utilizzando i tipi record possiamo racchiudere facilmente in un'unica variabile delle grandezze che descrivono un determinato oggetto o soggetto. Giusto per fare un esempio di come vengono definiti i tipi record, cercheremo di creare un nuovo tipo che permetta di contenere le informazioni riguardanti il nome, l'altezza e l'età di una persona:

type Persona = {nome: string; altezza: float; eta: int};;

Sostanzialmente abbiamo creato un nuovo tipo, chiamato Persona, che è caratterizzato da tre elementi: nome, altezza ed eta. Ma a differenza dei tipi enumerati e quelli unione, se vogliamo creare una nuova variabile di tipo Persona dobbiamo inizializzare tutti i suoi attributi:

let p = {nome="Tizio";altezza=172.4;eta=23};;

Bene, abbiamo visto come creare un tipo record, abbiamo anche visto come creare ed inizializzare una variabile del tipo creato, ma come fare ad utilizzare questi tipi nella programmazione? Per rispondere a questa domanda proviamo a scrivere una funzione che, data una lista di persone e dato il nome di una persona, ci restituisce una lista contenente tutte le persone che hanno il nome indicato:

let rec search lst x =
    match lst with
    | [] -> []
    | y::ys -> if y.nome=x then y::(search ys x)
               else (search ys x);;

Dal codice si può vedere che se la lista contiene almeno un elemento, la variabile locale y assume il valore del primo elemento della lista e visto che la lista è di tipo Persona, per conoscere il nome della prima persona della lista, viene usata la scrittura y.nome. In questo modo viene letto l'attributo nome della persona corrispondente. Se viene trovata una corrispondenza tra il nome della persona della lista e il nome da cercare, la persona viene aggiunta in testa ad una nuova lista, che alla fine verrà restituita come risultato.

Nessun commento:

Posta un commento