Scrieți o funcție care returnează câte cifre dintr-un număr sunt egale cu o anumită cifră, c dată și ea ca parametru.
Soluție Va trebui să numărăm 1 pentru fiecare cifră din număr egală cu cifra dată, și să nu numărăm (sau să numărăm 0) altfel. Comparația returnează o valoare booleană (false sau true), deci e utilă o funcție simplă care să convertească un boolean la întreg.
let int_of_bool = function false -> 0 true -> 1 val int_of_bool : bool -> int = <fun> (* tipul funcției, dedus de OCaml; nu se scrie în program *)Mai departe, ultima cifră din n e n mod 10, și putem converti rezultatul comparației n mod 10 = c din boolean în 1 sau 0. Dacă numărul e mai mic ca 10, acesta e și răspunsul; altfel, mai trebuie să apelăm recursiv funcția pentru a număra cifrele din restul numărului (partea de la cifra zecilor în față, deci n/10), și să adunăm acest număr.
let rec nrcif c n = int_of_bool (n mod 10 = c) + if n < 10 then 0 else nrcif c (n/10) val nrcif : int -> int -> int = <fun>În ML, if ... then ... else ... e o expresie, deci dacă rezultatul de pe cele două ramuri e un întreg, ca aici, putem s-o folosim direct în calcul ca orice întreg (de exemplu, în adunare).
let rec nrcif c n = if n < 10 then int_of_bool (n = c) else int_of_bool (n mod 10 = c) + nrcif c (n/10)De fapt, variantele scrise funcționează corect pentru numere nenegative. Pentru numere negative, s-ar intra pe ramura n < 10, indiferent de numărul de cifre. Deci, în funcție de comportamentul dorit, ar trebui fie semnalată o eroare, fie ar trebui definit ca soluție:
let nrcif_int n = nrcif (abs n)unde abs e funcția predefinită de valoare absolută pentru întregi.
Exercițiul 2. Funcția putere pentru exponent natural (și bază număr întreg) se poate scrie recursiv în felul următor:
let rec putere x n = if n = 0 then 1 else x * putere x (n-1)Similar cu cele de mai sus, definiți recursiv o funcție care ia ca parametru un întreg n și o funcție f, și returnează compunerea funcției f cu ea însăși de n ori: f °f ° ... °f. Verificați rezultatul cu valori concrete pentru f, n și valoarea în care se aplică funcția.
Soluție Putem defini întâi obișnuita compunere a două funcții:
let comp f g x = f (g x) val comp : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>Apoi trebuie să definim cazul de bază. Elementul identitate în raport cu operația de compunere e funcția identitate f(x) = x, adică fun x -> x . Apoi, definim recursiv f n = f °f n-1 pentru n > 0 .
let rec apply f n = if n = 0 then fun x -> x else comp f (apply f (n-1)) val apply : ('a -> 'a) -> int -> 'a -> 'a = <fun>Deoarece primul parametru, funcția f, e același la fiecare apel recursiv, putem defini în interior o funcție ajutătoare apply1, care are acces la parametrul lui apply și nu mai trebuie să-l transmită de fiecare dată:
let apply f = let rec apply1 n = if n = 0 then fun x -> x else comp f (apply1 (n-1)) in apply1Construcția let definitie = expr1 in expr2 (sau la fel, cu let rec) e la rândul ei o expresie, și poate fi folosită oriunde e nevoie de o expresie. Valoarea ei este expr2, în care definiție e folosită cu înțelesul din expr1 (substituită cu expr1). Definiția e făcută local, și poate fi folosită doar în expr2, nefiind vizibilă altundeva.
Atenție, f e un parametru al funcției apply, și deci nu trebuie să definim înainte o funcție let f = ..., tot după cum dacă definim let g x = x * x + 1 nu îl definim înainte pe x, ci furnizăm argumentul dorit la apel: g 1 (care, evaluat, dă 2).
Putem scrie deci direct apply (fun x -> x * x + 1) 3 0 care
calculează ((x2 + 1)2 + 1)2 + 1 în punctul 0, deci 5.
Desigur, putem scrie și din aproape în aproape:
let g x = x * x + 1 let h = apply g 3și apela în final h 0 cu același rezultat.
Cum în OCaml nu există un tip număr natural, parametrul numeric al funcției apply e de fapt un întreg; pentru un număr negativ, ea se va apela continuu cu n-1 până la depășirea stivei. Completăm funcția, semnalând printr-o excepție când primește un argument negativ:
let apply f n = let rec apply1 n = if n = 0 then fun x -> x else comp f (apply1 (n-1)) in if n < 0 then raise (Invalid_argument "apply") else apply1 nCuvântul cheie raise semnalează (produce) o excepție. Invalid_argument e o excepție predefinită, cu un parametru șir (de regulă numele funcției care a produs excepția sau un alt mesaj explicativ).
Marius Minea Last modified: Sat Oct 19 11:30:00 EEST 2013