190 likes | 315 Views
Llistes. L’estructura de dades llista és d’importància cabdal en els llenguatges funcionals. Són preferides als conjunts pels seus avantatges computacionals, a més de permetre la repetició de components. Les llistes són seqüències d’elements del mateix tipus, a diferència de LISP.
E N D
Llistes • L’estructura de dades llista és d’importància cabdal en els llenguatges funcionals. Són preferides als conjunts pels seus avantatges computacionals, a més de permetre la repetició de components. • Les llistes són seqüències d’elements del mateix tipus, a diferència de LISP. • Així, tenim: T és un tipus • ----------------------- • T list és un tipus int list (bool * string) list (bool -> int) list (int list) list ‘a list [1, 3, 56, 45] [(true “a”), (false “b”)] [(fn true => 2 | false =>3)] [[1, 2, 3], [2, 3, 2]] []
Constructors de llistes • La llista buida, anomenada ‘nil’, s’escriu: • [] : ‘a list • és de tipus polimòrfic, és a dir, representa la llista buida de qualsevol tipus. • La construcció de llistes es fa a partir de l’operador ‘cons’, que en SML s’escriu :: • :: : ‘a * ‘a list -> ‘a list’ Nil i cons són anomenats constructors de llistes. Juguen un paper dual, s’utilitzen en la descripció dels valors i són també els noms d’operacions. [3, 2, 4, 3] == 3::[2, 4, 3] == 3::2::[4, 3] == 3::2::4::[3] == 3::2::4::3::[]
Unicitat de la representació • 1) els valors de tipus T list es generen pels constructors [] i :: sobre valors de tipus T. • 2) les llistes són construides de forma única per :: i []. Això vol dir que [] no és igual a cap llista construida a partir de ::, i que a::x ==b::y si i nomes si a ==b i x ==y. • Els detalls d’implementació de llistes són amagats als programadors. No sabem quina seqüència de posicions de memòria conté la llista. • Així, canviar un component d’una llista no significa destruir els continguts d’una posició de memòria, sinò obtenir una nova llista a partir de l’anterior.
Operacions sobre llistes • @ : ‘a list * ‘a list -> ‘a list (infix) • rev: ‘a list -> ‘a list • explode: string -> string list • implode: string list -> string • link: (‘a list) list -> ‘a list • length: ‘a list -> int • hd: ‘a list -> ‘a • tl: ‘a list -> ‘a list • null: ‘a list -> bool • upto: int * int -> int list (infix) • sumlist: int list -> int • prodlist: int list -> int • doublelist: int list -> int list • copy: int -> ‘a -> ‘a list member: ‘‘a list ->‘‘a= -> bool map: (‘a -> ‘b) -> ‘a list -> ‘b list
Seccions • Algunes vegades necessitarem versions currificades dels constructors de llistes. Així definim • fun cons a x = a :: x • fun consonto x a = a :: x • fun append x y = x @ y • Hi ha una notació, anomenada secció, no permesa en SML, que no obligaria a definir les funcions anteriors: • cons a == (a::); consonto x == (::x) • Vegeu també una funció d’intercanvi C interessant • fun C f x y = f y x • així tindriem les equivalències: • val consonto = C cons • val rappend = C append
Combinacions • Amb les funcions introduides fins ara es poden definir una infinitat de noves funcions com a combinació d’elles • consr 3 [1, 2, 5] == [1, 2, 5, 3] • fun consr a x = rev(a::rev x) • fun consr a x = x @ [a] • palindrom [1, 2, 3] == [1, 2, 3, 3, 2, 1] • fun palindrom x = x@(rev x) • drop 2 [1, 2, 3, 4, 5] == [3, 4, 5] • fun drop n = repeat n tl • Una forma habitual de definir funcions sobre llistes és distingir dos casos, la llista buida i les construides a partir de l’operador ::. Aquesta és la forma habitual en LISP. • En SML hi ha una forma alternativa, l’acarament de patrons.
Acarament de patrons en llistes fun sumlist []=0 | sumlist (a::x) = a + sumlist x • Com les llistes són de la forma [] o bé a::x permetrem l’aparició de [] i :: en els patrons de definició de funcions. • En cada cas el paràmetre de la definició és un patró construït a partir de constructors i variables. Per exemple, compareu: fun sumlist x = if null x then 0 else hd x + sumlist (tl x) amb: Considereu l’equivalència de [a, b] amb a::b::[]
Definició de patró • Un patró és: • 1) una variable, o • 2) una constant, o • 3) un constructor aplicat a un patró. • amb la restricció que una variable només pot aparéixer una vegada en un patró. Els llenguatges funcionals funcionen per acarament de patrons i no per unificació. fun alternar (a::b::x) = b::alternar x | alternar [a] = [] | alternar [] = [] fun member (a::x) b=if a = b then true else member x b | member [] b = false
Funcions d’ordre superior • La forma habitual de manipulació de llistes és la recursió. Ara bé, és útil disposar de funcions (d’ordre superior) que facilitin aquesta manipulació per incrementar la llegibilitat del codi. fun map f (a::x) = (f a)::map f x | map f [] = [] map lengths [“una”,“llista”,“de”,“cadenes”] == [3,6,2,7] map g (map f x) == map (g o f) x == ((map g) o (map f)) x fun filter pred (a::x) = if pred a then a::filter pred xelse filter pred x | filter pred [] = []
Més funcions d’ordre superior • fun exists pred (a::x) = if pred a then true • else exists pred x • | exists pred [] = false • fun all pred (a::x) = if pred a then all pred x else false • | all pred [] = true • fun zip f [] [] = [] • | zip f (a::x) (b::y) = f a b :: zip f x y • | zip f x y = error “llistes de long. diferent” • val sumallistes = zip plus • val prodllistes = zip times • val parellllistes = zip pair
Acumular fun accumulate f a (b::x) = accumulate f (f a b) x | accumulate f a [] = a • Una altra funció habitual sobre llistes és iterar l’aplicació d’una funció i accumular el resultat en un sol valor. Vegeu la seva utilització: val sumlist= accumulate plus 0 val prodlist = accumulate times 1 val link = accumulate append [] val implode = accumulate concat ““ fun max a b = if a < b then b else a fun maxlist (a::x) = accumulate max a x | maxlist [] = error “maxim d’una llista buida”
Reduir • De vegades interessa associar arguments per a una funció binària per la dreta en comptes de per l’esquerra com en el cas anterior • f a1 (f a2 (... (f an a) ...)) en comptes de • f (...(f (f a a1) a2) ...) an fun reduce f a (al::rest) = f al (reduce f a rest) | reduce f a [] = a Aquestes dues funcions són duals, una es pot definir en funció de l’altre fent el reverse de la llista argument. Si la funció f és conmutativa, la funció accumulate i reduce són equivalents. Així, per exemple sumlist es pot definir de les dues maneres perque plus és conmutativa.
Ordenació de llistes • Veurem dos algorismes. el primer, i més senzill, consisteix en col.locar un element en una llista respectant l’ordre imposat per una funció (currificada) d’ordre. fun insertwrt before item [] = [item] | insertwrt before item (a::x) = if before a item then item::a::x else a::insertwrt before item x Noteu que la funció before ha de definir un ordre total, i per tant ha de ser transitiva, antisimètrica i o bé before a b = true o bé before b a = true. fun insert_sortwrt before = letfun insertin x a = insertwrt before a x in accumulate insertin [] end
Quicksort • Ara veurem un algorisme eficient d’ordenació de llistes, Primer una versió per ordenació de llistes d’enters i després una versió paramètrica. fun sort [] = [] | sort (a::x) = letval low = filter (lessthan a) x and high = filter (non (lessthan a)) x in sort low @ [a] @ sort high fun sortwrt before = letfun sort [] = [] | sort (a::x) = letval low = filter (before a) x and high = filter (non (before a)) x in sort low @ [a] @ sort high in sort end
Inducció i llistes • De vegades necessitarem verificar que certes propietats es verifiquen per a totes les llistes. Per exemple, • f x == g x per a tot x: ‘a list • La manera de fer-ho és via inducció estructural. La inducció estructural fa ús de l’estructura recursiva de les dades, en aquest cas llistes, per definir el pas d’inducció. • Principi d’inducció estructural per llistes: • Per tal de provar que P(x) es verifica per totes les llistes (finites i ben definides) x:T list • només cal provar: • (1) P([]) és cert • (2) P(a::y) se segueix d’assumir P(y) per a tot y :Tlist ben definida i finita, i per a tot element a:T ben definit.
Exemple d’inducció structural • Provarem que per a tota x: ‘a list • map f (map g x) = map (f o g) x • (1) (Cas base) map f (map g [])) = map f [] = [] = • = map (f o g) [] • (2) (Pas d’inducció) Per a qualsevol a : ‘a i y : ‘a list • If map f (map g y) = map (f o g) y (HI) llavors • map f (map g (a::y)) = map (f o g) (a::y) • map f (map g (a::y)) = map f (g a :: map g y) (Definició) • = f (g a) :: map f (map g y) • = f (g a) :: map (f o g) y (HI) • = (f o g) a :: map (f o g) y (definició de o) • = map (f o g) (a::y) (definició de map)
Generalització d’objectius • De vegades les proves per inducció fracassen per que la hipòtesi d’inducció és massa feble. La solució consisteix en generalitzar l’objectiu; fent-lo més fort. Veurem un exemple. Suposant les definicions: fun [] @ y = y | (a::x) @ y = a::(x @ y) fun revonto x [] = x | revonto x (a::y) = revonto (a::x) y fun rev x = revonto [] x Volem provar que per a tota x, y: ‘a list es verifica revonto y (rev x) = x @ y (OBJ)
Fracàs en la prova • Un intent directe de fer la prova ens conduirà a la necessitat de provar (en el pas d’inducció): • revonto y (revonto [a] x) = a :: (x @ y) • utilitzant l’expressió (derivada de la HI): • revonto y (revonto [] x) = x @ y • En aquest punt estem bloquejats. El problema és que necessitem generalitzar revonto [] x a la hipòtesi d’inducció en revonto r x per a qualsevol r:’a list. Això vol dir que l’objectiu s’ha de reformular • revonto y (revonto r x) = revonto (x @ y) r (G) • Obviament (G) implica (OBJ) • revonto y (rev x) = revonto y (revonto [] x) • = revonto (x @ y) [] (G amb r = []) • = x @ y
Prova de l’objectiu generalitzat • revonto y (revonto r x) = revonto (x @ y) r (G) • (1) (Cas base) (x = []) • revonto y (revonto r []) = revonto y r (def. revonto) • = revonto ([] @ y) r (def. @) • (2) (Pas d’inducció) (x = a::z) • revonto y (revonto r (a::z)) = • = revonto y (revonto (a::r) z) = • = revonto (z @ y) (a::r) = (per HI) • = revonto (a::(z @ y)) r = (def. revonto) • = revonto ((a::z) @ y) r (def. @) • Trobar la generalització adient en cada cas és la part més imaginativa de les proves per inducció.