ocaml: un veloce ripasso - dipartimento di...
TRANSCRIPT
OCaml:unveloceripasso
1
Los%lefunzionale
• In Java (ma anche in C) l’effetto osservabile di un programma è la modifica dello stato
• In Ocaml il risultato della computazione è la produzione di un nuovo valore
temp = pair.x; pair.x = pair.y; pair.y = temp;
let x = 5 in (x,x)
Value-orientedprogramming
• Programmazionefunzionale:“value-orientedprogramming”o unprogrammaOcamlèunaespressioneo unaespressioneOcamldenotaunvalore
• L’esecuzionediunprogrammaOCamlpuòesserevistacomeunasequenzadipassidicalcolo(semplificazionidiespressioni)cheallafineproduceunvalore
Espressioni
• Sintassi:regoledibuonaformazione• Seman%ca
o regoleditypechecking(%pooerrore)o regolediesecuzionechegaran%sconocheespressioni%pate
produconounvalore
Valori
• Unvaloreèunaespressionechenondeveesserevalutataulteriormenteo 34èunvaloredi%pointo 34+17èun’espressionedi%pointmanonèunvalore
Valori
• Lanotazione<exp>⇒<val>indicachelaespressione<exp>quandovalutatacalcolailvalore<val>
3⇒3(valoridibase)3+4⇒72*(4+5)⇒18
eval(e)=vmetanotazionepere⇒v
let
• Sintassi:letx=e1ine2o xiden%fiero e1,e2espressionio letx=e1ine2espressioneo x=e1binding
• letx=2inx+xletincx=x+1ininc10lety="programmazione"in(letz="2"iny^z)
let
• letx=e1ine2• Regoladivalutazione
o eval(e1)=v1o sos%tuireilvalorev1pertuZeleoccorrenzedixine2oZenendo
l’espressionee2’o subst(e2,x,v1)=e2’o eval(e2’)=vo eval(letx=e1ine2)=v
let
eval(e1) = v1 subst(e2, x,v1) = e2' eval(e2') = veval(let x = e1 in e2) = v
Esempio
• eval(letx=1+4inx*3)o eval(1+4)=5
• eval(letx=5inx*3)o subst(x*3,x,5)=5*3
• eval(5*3)=15• eval(letx=1+4inx*3)=15
letbinding=scope
letx=42in
(*ynonèvisibile*)
x+(lety="3110"in (*yèvisibile*)
int_of_stringy)
scope:overlapping
let x = 5 in ((let x = 6 in x) + x)
Duecasi ((let x = 6 in x) + 5) ((let x = 6 in 5) + 5)
scope:overlapping
let x = 5 in ((let x = 6 in x) + x)
Duecasi ((let x = 6 in x) + 5) ((let x = 6 in 5) + 5)
scope:overlapping
let x = 5 in ((let x = 6 in x) + x)
Duecasi ((let x = 6 in x) + 5) ((let x = 6 in 5) + 5)
Alphaconversione
• L’identità puntuale delle variabili legate non ha alcun senso!! • In matematica
– f(x) = x * x – f(y) = y * y
sono la medesima funzione!! • In informatica
– let x = 5 in ((let x = 6 in x) + x) – let x = 5 in ((let y = 6 in y) + x)
Dichiarazionedifunzioni
letf(x:int):int=lety=x*10iny*y;;
Funzioniricorsive
letrecpowxy=ify=0then1elsex*powx(y-1);;
Applicazionedifunzioni
letf(x:int):int=lety=x*10iny*y;;f5;;-:int=2500
Applicazionedifunzioni
letrecpowxy=ify=0then1elsex*powx(y-1);;pow23;;-:int8
Dichiarazione
• Lavalutazionediunadichiarazionediunafunzioneèlafunzionestessao lefunzionisonovalori
Applicazione
• eval(e0e1...en)=v'seo eval(e0)=letfx1...xn=eo eval(e1)=v1...eval(en)=vno subst(e,x1,…,xn,v1,...,vn)=e'o eval(e')=v'
Esempi
• letdouble(x:int):int=2*x;;• letsquare(x:int):int=x*x;;• letquad(x:int):int=double(doublex);;• letfourth(x:int):int=square(squarex)
Esempi
• lettwice((f:int->int),(x:int)):int=f(fx)
• letquad(x:int):int=twice(double,x)• letfourth(x:int):int=twice(square,x)
• twiceo higher-orderfunc%on:unafunzionedafunzioniadaltrivalori
Funzionihigher-order
• letcompose((f,g):(int->int)*(int->int))(x:int):int=f(gx)
• letrecn%mes((f,n):(int->int)*int)=ifn=0then(fun(x:int)->x)elsecompose(f,n%mes(f,n-1))
OCamlList
• letlst=[1;2;3];;• letempty=[];;• letlonger=5::lst;;• letanother=5::1::2::3::[]
List:sintassi
• []lalistavuota(nilderivatodalLISP)• e1::e2inseriscel’elementoe1intestaallalistae2(::=LISPcons)• [e1;e2;...;en]notazionesintafcaperlalistae1::e2::...::en::[]
Accedereaunalista
• StruZuralmenteunalistapuòessereo nil,[]o lalistaoZenutamedianteunaoperazionediconsdiunelementoa
unalista• Idea:usareilpaSernmatchingperaccedereaglielemen%dellalista• letemptylst=matchlstwith
|[]->true|h::t->false
Esempi
• letrecsumxs=matchxswith|[]->0|h::t->h+sumt
• letrecconcatss=matchsswith|[]->""|s::ss'->s^(concatss')
• letrecappendlst1lst2=matchlst1with|[]->lst2|h::t->h::(appendtlst2)
PaZernMatching
• matchewith|p1->e1|p2->e2|...|pn->en
• Leespressionipisichiamanopa6ern
PaZernmatching
• IlpaZern[]“match”solamenteilvalore[]• match[]with
|[]->0|h::t->1
(*res%tuisceilvalore0*)• match[]with
|h::t->0|[]->1
(*res%tuisceilvalore1*)
PaZernmatching
• Il pattern h::t “match” una qualsiasi lista con almeno un elemento, e inoltre ha l’effetto di legare quell’elemento alla variabile h e la lista rimanente alla variabile t
• match [1; 2; 3] with | [] -> 0 | h::t -> h (* restituisce il valore 1 *)
• match [1; 2; 3] with | [] -> [] | h::t -> t (* restituisce il valore [2; 3] *)
Altriesempi
• IlpaZerna::[]“match”tuZelelisteconesaZamenteunelemento• IlpaZerna::b“match”tuZelelisteconalmenounelemento• IlpaZerna::b::[]“match”tuZelelisteconesaZamentedueelemen%• IlpaZerna::b::c::d“match”tuZelelisteconalmenotreelemen%
Unesempiopiùcomplicato
• letrecdrop_valvlst=matchlstwith|[]->[]|h::t->lett'=drop_valvtin ifh=vthent'elseh::t'
Unaltroesempio
• let rec max_list = function | [] -> ??? | h::t -> max h (max_list t)
• Cosa mettiamo al posto di ??? ? • min_int è una scelta possibile ... • O sollevare una exception ... • In Java, avremmo potuto restituire null... • …ma siamo in Ocaml, che ci fornisce una altra soluzione
Op%ontype
• let rec max_list = function | [] -> None | h::t -> match max_list t with | None -> Some h | Some x -> Some (max h x)
(* max_list : 'a list -> 'a option *)
Iteratori
• let rec map f = function | [] -> [] | x::xs -> (f x)::(map f xs)
• map : ('a -> 'b) -> 'a list -> 'b list
• let is_even x = (x mod 2 = 0);; • let lst = map is_even [1; 2; 3; 4];;
Parametroimpicitodi%polista
Iteratori
• let rec map f = function | [] -> [] | x::xs -> (f x)::(map f xs)
• map : ('a -> 'b) -> 'a list -> 'b list
• let is_even x = (x mod 2 = 0);; • let lst = map is_even [1; 2; 3; 4];; • Risultato [false; true; false; true]
Definire%pididatoinOCaml
OcamlpermeZealprogrammatoredidefinirenuovi%pididato
type giorno = | Lunedi | Martedi | Mercoledi | Giovedi | Venerdi | Sabato | Domenica
Dichiarazione di tipo
Nome del tipo Costruttori
IcostruZoridefinisconoivaloridel%podidatoSabatoha%pogiorno[Venerdi,Sabato,Domenica]ha%pogiornolist
PaZernmatching
IlpaZernmatchingfornisceunmodoefficienteperaccedereaivaloridiun%podidato
let string_of_g (g : giorno) : string = begin match g with | Lunedi -> "Lunedi" | Martedi -> "Martedi" | : | : | Domenica -> "Domenica" end
IlpaZernmatchingseguelastruZurasintafcadeivaloridel%podidato:icostruZori
Astrazionisuida%
• Avremmo potuto rappresentare il tipo di dato giorno tramite dei semplici valori interi – Lunedi = 1, Martedi = 2, …, Domenica = 7
• Ma… – il tipo di dato primitivo int fornisce un insieme di operazioni differenti
da quelle significative sul tipo di dato giorno, Mercoledi – Domenica non avrebbe alcun senso
– esistono un numero maggiore di valori interi che di valori del tipo giorno
• Morale: I linguaggi di programmazione moderni (Java, C#, C++, Ocaml, …) forniscono strumenti per definire tipi di dato
OcamlType
• IcostruZoripossonotrasportare“valori”
# type foo = | Nothing | Int of int | Pair of int * int | String of string;; type foo = Nothing | Int of int | Pair of int * int | String of string
Nothing Int 3 Pair (4, 5) String "hello"...
Valoridel%pofoo
Dichiarazionedavalutare
Risultato
PaZernmatching
let get_count (f : foo) : int = begin match f with | Nothing -> 0 | Int(n) -> n | Pair(n,m) -> n + m | String(s) -> 0 end
Tipididatoricorsivi
# type tree = | Leaf of int | Node of tree * int * tree;; type tree = Leaf of int | Node of tree * int * tree
let t1 = Leaf 3 let t2 = Node(Leaf 3, 2, Leaf 4) let t3 = Node (Leaf 3, 2, Node (Leaf 5, 4, Leaf 6))
Tipididatoricorsivi
# type tree = | Leaf of int | Node of tree * int * tree;; type tree = Leaf of int | Node of tree * int * tree
Quanti di voi hanno programmato con strutture dati del tipo tree?
Alberibinari
Radice
foglie
SoZoalberosinistro
SoZoalberodestro
vuoto
Liavetevis%aalgoritmica!!!!
AlberibinariinOCaml
type tree = | Empty | Node of tree * int * tree
let t : tree = Node(Node(Empty, 1, Empty), 3 Node(Empty, 2, Node(Empty, 4, Empty)))
3
1 2
4
Ricercainunalbero
let rec contains (t : tree) (n : int) : bool = begin match t with | Empty -> false | Node(lt, x, rt) -> x = n || (contains lt n) || (contains rt n) end
LafunzionecontainseffeZuaunaricercadelvalorensull’alberotCasopeggiore:devevisitaretuZol’albero
Alberibinaridiricerca
Idea:ordinareida%suiqualivienefaZalaricercaUnalberobinariodiricerca(BST)èunalberobinariochedevesoddisfarealcuneproprietàinvarian%addizionali
• Node(lt,x,rt)èunBSTse• ltertsonoBST• tufinodidiltcontengonovalori<x• tufinodidirtcontengonovalori>x
• Empty(l’alberovuoto)èunBST
INVARIANTEDIRAPPRESENTAZIONE
Esempio
L’invariantedirappresentazionedeiBSTèsoddisfaZoComesidimostra?RicordateletecnichecheaveteimparatoaLPP!!!
RicercasuunBST
(*Ipotesi:tèunBST*)letreclookup(t:tree)(n:int):bool=beginmatchtwith|Empty->false|Node(lt,x,rt)->ifx=nthentrueelseifn<xthen(lookupltn)else(lookuprtn)end
Osservazione1:L’invariantedirappresentazioneguidalaricercaOsservazione2:Laricercapuòres%tuirevalorinoncorrefseapplicataaunalberochenonsoddisfal’invariantedirappresentazione
lookup(t,3)
ComecostruiamounBST
• Opzione 1 – costruiamo un albero e poi controlliamo (check) se vale l’invariante di
rappresentazione • Opzione 2
– definire le funzioni che costruiscono BST a partire da BST (ad esempio, la funzione che inserisce un elemento in un BST e restituisce un BST)
– definire una funzione che costruisce il BST vuoto – tutte queste funzioni soddisfano l’invariante di rappresentazione, pertanto “per
costruzione” otteniamo un BST – non si deve effettuare nessun controllo a posteriori!! – questo passo mette in evidenza il ruolo della teoria in informatica (tipi
algebrici): ne parleremo nel seguito del corso
insert(t,4)
insert(t,4)
insert
(*InsertnnelBSTt*)letrecinsert(t:tree)(n:int):tree=beginmatchtwith|Empty->Node(Empty,n,Empty)|Node(lt,x,rt)->
ifx=nthent elseifn<xthenNode(insertltn,x,rt) elseNode(lt,x,insertrtn)
end
Perqualemo%vol’alberocostruitodallafunzioneinsertèunBST?
delete(t,5)
L’operazionedirimozioneèpiùcomplicata:sidevepromuoverelafoglia3aradicedell’albero!!!
Funzioneausiliaria
letrectree_max(t:tree):int=beginmatchtwith|Node(_,x,Empty)->x|Node(_,_,rt)->tree_maxrt|_->failwith"tree_maxcalledonEmpty"end
L’invariantedirappresentazionegaran%scecheilvaloremaxsitrovanellapartepiùadestradell’albero
delete
letrecdelete(t:tree)(n:int):tree=beginmatchtwith|Empty->Empty|Node(lt,x,rt)->
ifx=nthenbeginmatch(lt,rt)with
|(Empty,Empty)->Empty |(Node_,Empty)->lt |(Empty,Node_)->rt |_->letm=tree_maxltin Node(deleteltm,m,rt) end
elseifn<xthenNode(deleteltn,x,rt) elseNode(lt,x,deletertn)
end
Funzionigeneriche
letreclength(l:intlist):int=beginmatchlwith|[]->0|_::tl->1+lengthtlend
letreclength(l:stringlist):int=beginmatchlwith|[]->0|_::tl->1+lengthtlend
Analizziamolafunzionelengthapplicataaintlistestringlist
Lefunzionisonoiden%che,ecceZuatal’annotazionedi%po
GenericiinOCaml
letreclength(l:'alist):int=beginmatchlwith|[]->0|_::tl->1+(lengthtl)end
Lanotazione'alistindicaunalistagenericalength[1;2;3]applicalafunzioneaintlistlength["a";"b";"c"]applicalafunzioneastringlist
Appendgenerico
letrecappend(l1:'alist)(l2:'alist):'alist=beginmatchl1with|[]->l2|h::tl->h::(appendtll2)end
PaZernmatchingpermeZedioperaresu%pigenericihha%po'atlha%po'alist
Genericzip
letreczip(l1:'alist)(l2:'blist):('a*'b)list=beginmatch(l1,l2)with|(h1::t1,h2::t2)->(h1,h2)::(zipt1t2)|_->[]end
Lafunzioneoperasu%pigenericimul%pli(da'aliste'blistverso('a*'b)list)zip[1;2;3]["a";"b";"c"]=[(1,"a");(2,"b");(3,"c")]:(int*string)list
Generictree
type'atree=|Empty|Nodeof'atree*'a*'atree
Sino%l’u%lizzodelparametrodi%po'a
GenericBST
letrecinsert(t:'atree)(n:'a):'atree=beginmatchtwith|Empty->Node(Empty,n,Empty)|Node(lt,x,rt)->
ifx=nthent elseifn<xthenNode(insertltn,x,rt) elseNode(lt,x,insertrtn)
end
Glioperatoridirelazione=e<operanosuogni%podidato
Collec%on(Set)
• Un insieme è una collezione di dati omogenei con operazioni di unione, intersezione, etc.
• Un Set è sostanzialmente una lista nella quale – la struttura d’ordine non è importante – non sono presenti duplicati ma non è un tipo primitivo in Ocaml
• Strutture dati come Set sono usate frequentemente in molte applicazioni – interrogazioni SQL (insieme degli studenti iscritti a Informatica,
insieme dei risultati di una ricerca sul web con Google, insieme dei dati di un esperimento al CERN, …)
• Diversi modi per implementare Set
Set
• UnBSTdefinisceunaimplementazionedellastruZuraSeto l’insiemevuoto(bstempty)o determinaretu@glielemen%cheappartengonoall’insieme(visita
dell’albero)o definireunaoperazionepertestarel’appartenenzadiunelementoa
uninsieme(lookup)o definireunioneeintersezione(tramiteleoperazionidiinsertedelete)
OCaml:SetInterface
moduletypeSet=sigtype'asetvalempty:'asetvaladd:'a->'aset->'asetvalremove:'a->'aset->'asetvallist_to_set:'alist->'asetvalmember:'a->'aset->boolvalelements:'aset->'alistend
Moduletype(inunfile.mli)perdichiarareunTdAsig...endracchiudonounasegnatura,chedefinisceilTdAeleoperazionival:nomedeivalorichedevonoesseredefini%edeiloro%pi
Idea(solita):fornirediversefunzionalitànascondendolaloroimplementazione
ModuliinOCaml
moduleMyset:Set=struct…(*implementa%onsofalltheopera%ons*):end
NomedelmoduloSignaturechedeveessereimplementata
“dotnota%on”
lets1=Myset.add3Myset.emptylets2=Myset.add4Myset.emptylets3=Myset.add4s1lettest():bool=(Myset.member3s1)=true;;run_test"Myset.member3s1"testlettest():bool=(Myset.member4s3)=true;;run_test"Myset.member4s3"test
Openmodule
Alterna%va:aprireloscopedelmodulo(open)perportareinominell’ambientedelprogrammainesecuzione
;;openMysetlets1=add3emptylets2=add4emptylets3=add4s1lettest():bool=(member3s1)=true;;run_test"Myset.member3s1"testlettest():bool=(member4s3)=true;;run_test"Myset.member4s3"test
Implementazionebasatasulist
moduleMySet2:Set=structtype'aset='alistletempty:'aset=[]...end
Una definizione concreta
per il tipo Set
Domande
openMySetlets1:intset=Empty
Superalafasedicontrollodei%pi?
Domande
openMySetlets1:intset=Empty
Superalafasedicontrollodei%pi?No:ilcostuZoreEmptynonèvisibileesternamentealmodulo!!!
CompilareprogrammiOCaml
ocamlc
program.ml (sorgente)
program.cmo (bytecode) a.out (bytecode eseguibile)
hZp://caml.inria.fr/pub/docs/manual-ocaml/comp.html
EseguirebytecodeOCaml
ocamlrun
hZp://caml.inria.fr/pub/docs/manual-ocaml-400/manual024.html
a.out (bytecode eseguibile)
result