Potrivirea de şabloane
Potrivirea de şabloane este un mecanism de selecţie flexibilă
şi foarte puternică prin care dependent de valoarea unei expresii
se selectează valoarea rezultatului. Mecanismul de bază
este similar instrucţiunii case din Pascal
sau switch din C, dar oferă facilităţi
mai puternice.
Pentru potrivirea de şabloane există mai multe mecanisme ML.
MATCH
Sintaxă:
match expr with
| p1 -> expr1
...
| pn -> exprn
Instucţiunea match evaluează expresia
expr şi pe rănd îl compară
cu probele p1...pn. În caz de potrivirea rezultatului
cu pi , match va
rezulta expri.
mynot |
# let mynot p = match p with true -> false
| false -> true;;
val mynot : bool -> bool = <fun> |
Este indicat ca probele să acopere întregul domeniu de
valori a expresiei de testat. CAML verifică acest lucru şi
avertizează în cazul incompletitudinii lui match.
Verificarea complectitudinii cazurilor de test |
# let test p = match p with 0 ->
true | 1 -> false;;
Characters 13-48:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
2
let test p = match p with 0 -> true | 1 -> false;;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
val test : int -> bool = <fun> |
Sau exclusiv 1 |
# let xor p = match p with
(true,true) -> false
| (true,false) -> true
| (false,true) -> true
| (false,false) -> false;;
val xor : bool * bool -> bool = <fun>
# xor (true, false);;
- : bool = true
|
Expresiile pot conţine şi variabile. Se numeşte şablon
linear un şablon care conţine variabile care apar maxim odată
în şablon.
Sau exclusiv 2 |
Este permisă
# let xor p = match p with
(true, x) -> not
x
| (false, x) -> x;;
val xor : bool * bool -> bool = <fun>
însă următoarea expresie nu mai este permisă:
# let xor p = match p with
(x, x) -> false
| (false, true) -> true
| (true, false) -> true;;
Characters 33-34:
(x, x) -> false
^
This variable is bound several times in this matching
|
Compactarea cazurilor
Semnul _ este şablonul oarecare,
valoarea ei coincide cu orice expresie.
Sau exclusiv 3 |
# let xor p = match p with
(false, true) -> true
| (true, false) -> true
| _ -> false;;
val xor : bool * bool -> bool = <fun>
|
Prin utilizarea semnului | (pipe) este posibil combinarea a mai multor
şabloane a căror rezultat este identic.
Test dacă un întreg este o cifră zecimală |
# let cifra n = match n with
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ->
true
| _ -> false;;
val cifra : int -> bool = <fun>
|
Potrivirea de şabloane la parametrii de
funcţiilor
Folosirea potrivirii de şabloane în definirea funcţiilor
cu mai multe cazuri este esenţială în ML.
Sintaxa mai precisă a lui function, prezentată
în lucrarea precedentă este:
function | p1 -> expr1
| p2 -> expr2
...
| pn -> exprn
Acestă sintaxă fiind echivalentă de fapt cu
function expr -> match expr with
p1
-> expr1
| p2 -> expr2
| ...
| pn -> exprn
Test pentru 0 şi 1 |
# let test01 = function | 0 -> true
| 1 -> true
| _ -> false;;
val test01 : int -> bool = <fun>
# test01 1;;
- : bool = true
# test01 2;;
- : bool = false
|
Potrivirea de şabloane la liste
Algoritmi de manipularea listelor pot fi foarte uşor implementaţi
folosind protrivirea de şabloane. În acest proces este foarte benefic
folosirea în scrierea şabloanelor operatorilor de manipularea listelor
:: şi @.
Calcularea lungimii unei liste |
# let rec length p = match p with
[] -> 0
| head::tail -> (1+length tail);;
|
Problema 1. |
Definiţi funcţiile myand
şi myor folosind potrivirea de
şabloane. |
Problema 2. |
Scrieţi funcţiile headoflist
şi tailoflist folosind mecanismul
de potrivirea şabloanelor. Nu se vor folosi List.hd sau List.tl. |
Problema 3. |
Scrieţi funcţia reverse
care inversează elementele unei liste. Nu se vor folosi List.hd, List.tl sau funcţiile de la Problema 2. |
Problema 4. |
Scrieti funcţiile rotate_left
şi rotate_right care rotesc spre
stânga respectiv spre dreapta o listă. La rotate_left nu se vor folosi List.hd , List.tl sau funcţiile de la Problema 2. |
Problema 5. |
Scrieţi funcţia maxoflist
care determină maximul unei liste cu elemente de tip oarecare
folosind mecanismul de potrivirea şabloanelor. Nu se vor folosi List.hd , List.tl sau funcţiile de la Problema 2. |
Problema 6. |
Scrieţi o funcţie apply
f i [l1::l2::..ln] care primeşte o funcţie f,
o valoare iniţială i şi
o listă, şi retunează: f(l1,
f(l2, ... f(ln1, f(ln,i))...)). Nu se vor folosi List.hd , List.tl sau funcţiile de la Problema 2.
# let u x y = x + y;;
# let f x y = 2 * x + y;;
# apply u 0 [1;2;3;4];;
- : int = 10
# apply f 1 [1;2;3;4];;
- : int = 21
|
Declaraţii de tipuri
Noi tipuri pot fi declarate folosind cuvântul cheie type.
Sintaxa:
type tip1 = typedef1
and tip2 = typedef2
...
and tipn = typedefn ;;
Unde tipul tipi va fi echivalent
cu tipul typedefi.
Declaraţii de tipuri |
# type t1 = (int*int)
and t2 = (int*char);;
type t1 = int * int
type t2 = int * char
|
Este permis declarearea tipurilor parametrizate:
type 'a tip = typedef ;;
type ('a1 ...'an) tip = typedef ;;
Tip parametrizat |
Exemplul următor declară tipul pereche,
care este format din asocierea unei întreg cu un tip specificat
de utilizatorul lui pereche.
# type 'tip pereche = int * 'tip;;
type 'a pereche = int * 'a
|
Specializarea unei tip mai genereal este deasemenea posibilă.
Specializarea tipurilor |
# type pereche_char = char pereche;;
type pereche_char = char pereche
|
Petru a indica tipul unei variabile, în caz că tipul nu
se poate deduce este necesar specifcarea lui explicită.
Explicitarea tipurilor |
# let variabila=(1,'a');;
val variabila : int * char = (1, 'a')
# let (variabila:pereche_char)=(1,'a');;
val variabila : pereche_char = (1, 'a')
|
Articolul
N-tupurile nu oferă flexibilitate suficientă. La fel ca
în Pascal sau C există şi în ML posibilitatea
utilizării tipului de articol, articolul fiind un conglomerat de
tipuri, fiecare element (câmp) a articolului având propria
nume. Declaraţia unei articole sa face după sintaxa:
type articol = { camp1 : tip1; ...; campn : tipn
} ;;
Număr raţional |
# type numarrat = { num: float ; den : float};;
type numarrat = { num : float; den : float; }
|
Atribuirea valorii cămpurilor articolului se face asignând
valori câmpurilor articolului într-o ordine arbitrară:
{ campi1 = expr1; ...; campin = exprn } ;;
Număr raţional |
# let numar = {den =2.;num = 3.};;
val numar : numarrat = {num = 3.; den = 2.}
# numar = {num = 1.+.2.; den = 2.};;
- : bool = true
|
Accesarea valorii unei câmp se poate face prin notaţia
obişnuită cu punct.
O alternativă este potrivirea şabloanelor conform sintaxei:
{ namei = pi ; ...; namej = pj }
Unde pi sunt şabloane formate de obicei
din variabile. Nu este necesar enumerarea tuturor câmpurilor articolului.
Incrementearea cu 1 unui număr raţional |
# let increment p = {num = p.num +. p.den; den
= p.den};;
val increment : numarrat -> numarrat = <fun>
# let increment p = match p with
{ num = n ; den = d} -> {num = n+.d; den
= d};;
val increment : numarrat -> numarrat = <fun>
# increment {num = 4.; den = 2.};;
- : numarrat = {num = 6.; den = 2.}
|
Tipuri cu variante
Tipurile cu variante sunt asemănatoare tipului union
din C, sau a articolului cu variante din Pascal: dependent de un selector
tipul are o anumita structură cu diferite câmpuri.
type nume = ...
| Constructori ...
| Constructorj of tipj ...
| Constructork of tipk * ...* tipl ...;;
Constructorx se numeşte constructor
şi este un identificator special cu care se identifică structura
curentă a variabilei. Contructorul trebuie totdeauna să
începe cu majusculă.
Declaraţii de tipuri cu variante |
# type calificativ = Admis | Respins;;
type calificativ = Admis | Respins
# type nota = Patru | Cinci | Sase | Sapte | Opt | Noua | Zece;;
type nota = Patru | Cinci | Sase | Sapte | Opt | Noua |
Zece
# type examen = Examen of nota*prezentare
|
Colocviu of calificativ;;
# type prezentare = int;;
type prezentare = int
# type examen = Examen of nota * prezentare
|
Colocviu of calificativ
type examen = Examen of nota * prezentare |
Colocviu of calificativ
|
Iniţializarea unei variabile se face folosind constructorul specificând
şi valorile corespunzătoare variantei.
Instanţierea tipurilor cu variante |
# let practica = Colocviu Admis;;
val practica : examen = Colocviu Admis
# let cflp1 = Examen(Zece,1);;
val cflp1 : examen = Examen (Zece, 1)
|
Procesarea variabilelor cu variante se face prin potrivirea şabloanelor.
Conversia tipului examen la string |
# let string_of_calificativ = function
Admis -> "admis."
| Respins -> "respins.";;
val string_of_calificativ : calificativ -> string = <fun>
# let string_of_nota = function
Patru -> "patru"
| Cinci -> "cinci"
| Sase -> "sase"
| Sapte -> "sapte"
| Opt -> "opt"
| Noua -> "noua"
| Zece -> "zece";;
val string_of_nota : nota -> string = <fun>
# let string_of_examen = function
Colocviu calif -> "Colocviul
cu calificativ " ^
string_of_calificativ calif
| Examen (n,p) -> "Prezentarea
" ^
string_of_int p ^ "
cu nota " ^ string_of_nota n;;
val string_of_examen : examen -> string = <fun>
# string_of_examen cflp1;;
- : string = "Prezentarea 1 cu nota zece"
|
Problema 7. |
Definiţi tipul nrcomplex
şi definiţi funcţiile de adunare şi înmulţire
între numere complexe. |
Probleme
Problema
1. Myand şi myor
Problema
2. Head şi tail
Problema
3. Reverse
Problema
4. Rotate left / right
Problema
5. Maximul unei liste
Problema
6. Apply
Problema 7. Complex
|