Cflp

Laborator

 

Facultatea de Automatica şi Calculatoare

Departamentul de Calculatoare


 
  Lucrarea 8 Lucrarea 9 Lucrarea 10 Lucrarea 11 Lucrarea 12 Lucrarea 13 Proiect Orar


Lucrarea 11

Subiecte

  

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