Sunteți pe pagina 1din 85

Preciz ri:

ˆ Primele 9 subiecte au ecare câte 10p. Cele 3 subpuncte ale problemei au ecare câte
10p. Punctajul NU se acord în absenta justic rii r spunsului! ,

ˆ Este sucient rezolvarea a 10 itemi din cei 12 pentru nota maxim .


ˆ Punctajul suplimentar reprezint bonus, ce poate compensa punctajul de pe parcurs.

1. Fie denitia si aplicatia functiei Racket de mai jos. Descrieti pas cu pas cum decurge
, , , , ,

evaluarea aplicatiei, utilizând modelul bazat pe substitutie textual .


, ,

1 (define (f x y)
2 (lambda (z)
3 (if (< x 10) (+ x y) z)))
4

5 > ((f (+ 0 1) (+ 2 3)) (+ 4 5))

Solut, ie. .
1 ((f (+ 0 1) (+ 2 3)) (+ 4 5))
2 → ((f 1 5) (+ 4 5))
3 → ((lambda (z) (if (< 1 10) (+ 1 5) z)) (+ 4 5))
4 → ((lambda (z) (if (< 1 10) (+ 1 5) z)) 9)
5 → (if (< 1 10) (+ 1 5) 9)
6 → (if true (+ 1 5) 9)
7 → (+ 1 5)
8 → 6

2. (V. sub. 1) Fie denitia si aplicatia functiei Haskell de mai jos. Descrieti pas cu pas
, , , , ,

cum decurge evaluarea aplicatiei, utilizând modelul bazat pe substitutie textual .


, ,

1 f x y z = if x < 10 then x + y else z


2

3 > f (0 + 1) (2 + 3) (4 + 5)

Solut, ie. .
1 f (0 + 1) (2 + 3) (4 + 5)
2 → if (0 + 1) < 10 then (0 + 1) + (2 + 3) else (4 + 5)
3 → if 1 < 10 then 1 + (2 + 3) else (4 + 5)
4 → if True then 1 + (2 + 3) else (4 + 5)
5 → 1 + (2 + 3)
6 → 1 + 5
7 → 6

3. Fie urm toarea functie Racket:,

1 (define (f x)
2 (if (<= x 0) 0
3 (- (f (- x 1)) 1))) ; (f (- (f (- x 1)) 1))))

(a) Ce fel de recursivitate utilizeaz functia? ,

1
(b) Ce fel de recursivitate utilizeaz functia, dac înlocuim ultima linie cu expresia
,

comentat ?

Solut, ie. .

(a) Pe stiv , întrucât se mai realizeaz operatii dup evaluarea aplicatiei recursive.
, ,

(b) Tot pe stiv , datorit aplicatiei recursive interne.


,

4. Ce aseaz prgramul Racket de mai jos?


,

1 (define (x x)
2 (let ([x x])
3 (x x)))
4

5 (x 0)

.
Solut, ie.

Eroare, deoarece aparitiile lui x din linia 3 se refer la parametrul functiei, 0, care nu
, ,

poate  aplicat ca o functie.,

5. Sintetizati tipul urm toarei functii Haskell:


, ,

1 f u = map . u

Tipul operatorului de compunere este


1 (.) :: (b -> c) -> (a -> b) -> a -> c

Solut, ie. .
1 f :: d -> e
2 u :: d = a -> b
3 map :: (g -> h) -> [g] -> [h]
4 b = g -> h
5 c = [g] -> [h]
6 e = a -> c
7 f :: (a -> g -> h) -> a -> [g] -> [h]

6. Supraînc rcati în Haskell operatorul (<=) pe liste, astfel încât o list este mai mic sau
,

egal decât alta, dac acelasi lucru se poate arma si despre sumele elementelor lor.
, ,

Exemplu: [1,2,10] <= [0, 1, 100, 3], întrucât 13 <= 104.

Solut, ie. .
1 instance (Num a, Ord a) => Ord [a] where
2 xs <= ys = sum xs <= sum ys

7. Câte interpret ri satisfac propozitiile α, respectiv β , în logica propozitional , unde p1 , . . . , p10


, ,

sunt propozitii simple?


,

α = p1 ∨ . . . ∨ p10
β = (p1 ∨ . . . ∨ p5 ) ∧ (p6 ∨ . . . ∨ p10 )

2
Solut, ie. .
Pentru n propozitii simple, exist 2n interpret ri distincte.
,

Toate interpret rile satisfac α, cu exceptia celei care asociaz Fals ec rei propozitii sim-
, ,

ple. Num rul nal este 210 − 1.


Similar se poate judeca si în cazul celor dou paranteze din β . Num rul nal este (25 −1)2 .
,

8. Utilizând metoda rezolutiei, demonstrati c : , ,

{∀x.P (x) ∨ ∀x.Q(x)} |= ∀x.(P (x) ∨ Q(x)).

.
Solut, ie.

Pentru claritate, redenumim, variabilele, obtinând: ,

{∀x.P (x) ∨ ∀y.Q(y)} |= ∀z.(P (z) ∨ Q(z)).

Utiliz m reducerea la absurd, conform c reia ad ug m negatia concluziei la multimea de , ,

propozitii: ¬∀z.(P (z) ∨ Q(z)) ≡ ∃z.(¬P (z) ∧ ¬Q(z)). Astfel, în urma aplic rii procedurii
,

de transformare în forma normal conjunctiv , care presupune si înlocuirea variabilei cu- ,

anticate existential z cu constanta cz , se obtin clauzele {P (x), Q(y)}, {¬P (cz )}, {¬Q(cz )}.
, ,

Aplicând rezolutia în raport cu substitutia {x ← cz , y ← cz }, se obtine clauza vid .


, , ,

9. Fie urm torul program Prolog:


1 a(1). a(2).
2 b(1). b(2).
3

4 c(X, Y) :- a(X), b(Y).


5 c(3, 4).
6 c(4, 3).

Unde ar trebui plasat operatorul cut, astfel încât scopul urm tor s e satisf cut?
1 ?- findall(_, c(X, Y), L), length(L, 4).

Solut, ie. .
1 c(X, Y) :- !, a(X), b(Y).

Astfel, se genereaz doar cele patru combinatii între a si b, renuntându-se la denitiile , , , ,

din liniile 5 si 6. ,

10. PROBLEMA. Puteti utiliza oricare dintre cele dou limbaje functionale studiate.
, ,

(a) Deniti reprezentarea unui arbore oarecare nevid, în care un nod poate avea oricâti
, ,

copii.
(b) Deniti o functie care calculeaz în ltimea arborelui. În ltimea unei frunze este 0.
, , , ,

Utilizati cel putin o functional .


, , ,

(c) Deniti o functie care calculeaz num rul de frunze din arbore. Utilizati cel putin o
, , , ,

functional . ,

3
.
Solut, ie.

Exemplic m solutia în Haskell.


,

1 data Tree a = Node a [Tree a]


2 deriving (Eq, Show)
3

4 height :: Tree a -> Int


5 height (Node _ []) = 0
6 height (Node _ children) = 1 + maximum (map height children)
7

8 leaves :: Tree a -> Int


9 leaves (Node _ []) = 1
10 leaves (Node _ children) = sum (map leaves children)

4
Preciz ri:
ˆ Primele 9 subiecte au ecare câte 10p. Cele 3 cerinte ale problemei au ecare câte 10p.
,

Punctajul NU se acord în absenta justic rii r spunsului! ,

ˆ Este sucient rezolvarea a 10 itemi din cei 12 pentru nota maxim .


ˆ Punctajul suplimentar reprezint bonus, ce poate compensa punctajul de pe parcurs.

1. Fie λ-expresia de mai jos:


E ≡ (λx.λy.(x y) y)

(a) (4p) Completati fraza: Pentru a evalua expresia utilizând modelul bazat pe substitutie
, ,

textual , trebuie înlocuite aparitiile (libere/legate?) _(1)_ ale variabilei _(2)_ din
,

expresia _(3)_ cu expresia _(4)_.


(b) (3p) Care este rezultatul evalu rii si de ce nu este corect? ,

(c) (3p) Ce pas lipseste pentru a obtine rezultatul corect? Scrieti rezultatul corect.
, , ,

Solut, ie. .

(a) (1) = libere, (2) = x, (3) = λy.(x y), (4) = y .


(b) Evaluarea mecanic ar duce la rezultatul λy.(y y), eronat, întrucât aparitia lui y ,

din dreapta lui E îsi schimb starea din liber în legat .


,

(c) Trebuie redenumit aparitia lui y din stânga lui E în z , obtinând λz.(y z), cu
, ,

conservarea st rii lui y .

Barem.

(a) ˆ 4 × 1p/completare corect


(b) ˆ 1p rezultatul gresit ,

ˆ 2p justicare
(c) ˆ 1p rezultatul corect
ˆ 2p justicare

2. Scrieti în Racket o functie recursiv pe stiv care s compun functia f cu ea îns si


, , , ,

de n ori, întorcând functia rezultant . De exemplu, ((multicompose 5 add1) 0) se


,

evalueaz la 5. Atentie! Folositi o form de let astfel încât functia f s NU e trimis


, , ,

ca parametru la ecare aplicatie recursiv . ,

Solut, ie. .
1 (define (multicompose n f)
2 (let loop ([n n])
3 (if (zero? n)
4 (lambda (x) x)
5 (compose f (loop (- n 1))))))

Barem.

ˆ 7p functionare corect
,

1
 1p conditie caz de baz
,

 2p rezultat caz de baz


 4p aplicatie recursiv
,

ˆ 3p named let
 1p parametru
 2p evitarea trimiterii lui f ca parametru la aplicatia recursiv
,

3. Scrieti în Racket, folosind exclusiv functionale, o functie care ia ca parametri o list de


, , ,

functii si o list de argumente, posibil de lungimi diferite, si aplic ecare functie pe ecare
, , , ,

argument, pe principiul unui produs cartezian, întorcând lista rezultatelor. De exemplu,


(cartesian-app (list add1 sub1 odd?) (list 1 2)) se evalueaz la ’(2 3 0 1
#t #f). Primele dou elemente ale rezultatului, 2 si 3, corespund aplicatiilor (add1 1)
, ,

si (add1 2); urm toarele dou , 0 si 1, aplicatiilor (sub1 1) si (sub1 2) etc.


, , , ,

Solut, ie. .
1 (define (cartesian-app fs xs)
2 (apply append
3 (map (lambda (f)
4 (map (lambda (x) (f x)) xs))
5 fs)))

Barem.

ˆ 3p parcurgere list de functii cu map sau fold


,

ˆ 3p parcurgere list de argumente cu map sau fold


ˆ 1p aplicare functie individual pe argument individual
,

ˆ 3p aplatizare list

4. S presupunem c în Racket exist functia stream-merge, care primeste dou uxuri


, ,

pe care le îmbin alternându-le elementele. De exemplu, (stream-merge (stream 0 2


4 ...) (stream 1 3 5 ...)) se evalueaz la (stream 0 1 2 3 4 5 ...), adic la
naturals.
Determinati primele 8 elemente ale uxului s:
,

1 (define s
2 (stream-cons 'x (stream-merge naturals s)))

.
Solut, ie.

Îmbin m uxurile naturals si s, iar dac ad ug m ’x în fata rezultatului, obtinem s:


, , ,

0 1 2 3 ... (stream-merge)
s0 s1 s2 s3 ...
---------------------------
x | 0 s0 1 s1 2 s2 3 s3 ... =
s0 s1 s2 s3 s4 s5 s6 s7 ... =
x 0 x 1 0 2 x 3

2
Barem.

ˆ 6p ideea rezolv rii


 1p explicitare simbolic elemente s
 2p îmbinare cu naturals
 1p ad ugare x
 2p corespondent elemente între s si denitia lui s
, , ,

ˆ 4p elementele (0,5p × 8)

5. Sintetizati tipul urm toarei functii în Haskell:


, ,

1 f xs = head xs xs

Solut, ie. .
1 f :: a -> b
2 xs :: a
3 head :: [c] -> c
4 a = [c]
5 head xs :: c = d -> e
6 d = [c]
7 c = [c] -> e

Eroare de tip, întrucât s-ar obtine un tip innit, dac c s-ar lega la o expresie de tip care
,

îl contine strict.
,

Barem.

ˆ 1p tip initial f si xs
, ,

ˆ 1p tip head
ˆ 1p unicare tip parametru formal/actual head (linia 4)
ˆ 1p tip head xs
ˆ 1p unicare tip parametru formal/actual head xs (linia 6)
ˆ 2p unicare ciclic (linia 7)
ˆ 3p conchidere eroare

6. Generalizati functionala filter din Haskell, având tipul (a -> Bool) -> [a] -> [a],
, ,

pentru a opera pe orice constructor de tip unar ce utilizeaz valori de tipul a, nu numai
pe constructorul list .
(a) (3p) Deniti clasa Filterable, parametrizat cum considerati de cuviint , în care
, , ,

s deniti functia filter’, cu tipul adecvat.


, ,

(b) (2p) Instantiati Filterable, denit mai sus, cu constructorul list standard.
, ,

(c) (5p) Instantiati Filterable, denit mai sus, cu constructorul de tip Maybe,
, ,

denit prin
1 data Maybe a = Just a | Nothing

Solut, ie. .

3
1 class Filterable t where
2 filter’ :: (a -> Bool) -> t a -> t a
3

4 instance Filterable [] where


5 filter’ = filter
6

7 instance Filterable Maybe where


8 filter’ _ Nothing = Nothing
9 filter’ f j@(Just a) = if f a then j else Nothing

Barem.

(a) ˆ 1p antet
ˆ 2p aplicare corect constructor de tip pe variabil de tip, t a
(b) ˆ 1p antet, instantiere cu [], nu cu [a]
,

ˆ 1p denitie filter’,

(c) ˆ 1p antet, instantiere cu Maybe, nu cu Maybe a


,

ˆ 1p caz Nothing
ˆ 1p caz Just, conditie ,

ˆ 1p caz Just, rezultat pt satisfacere conditie ,

ˆ 1p caz Just, rezultat pt nesatisfacere conditie ,

7. Demonstrati utilizând rezolutia si reducerea la absurd c propozitia


, , , ,

∀x.(P (x) ⇒ Q(x))

are drept consecint logic propozitia


, ,

∃y.P (y) ⇒ ∃z.Q(z).

(a) (2p) Aduceti prima propozitie la forma clauzal .


, ,

(b) (5p) Negati a doua propozitie si aduceti rezultatul la forma clauzal .


, , , ,

(c) (3p) Aplicati rezolutia pe clauzele obtinute anterior.


, , ,

Solut, ie. .

(a) Rescriem implicatia si elimin m cuanticatorul universal, si obtinem ¬P (x) ∨ Q(x),


, , , ,

corespunzând clauzei {¬P (x), Q(x)} (1).


(b) Neg m concluzia si transform m în forma clauzal urmând pasii:
, ,

¬(∃y.P (y) ⇒ ∃z.Q(z))


¬(¬∃y.P (y) ∨ ∃z.Q(z))
∃y.P (y) ∧ ¬∃z.Q(z)
∃y.P (y) ∧ ∀z.¬Q(z)
P (cy ) ∧ ¬Q(z)

Se obtin clauzele {P (cy )} (2) si {¬Q(z)} (3), unde cy este constanta obtinut prin
, , ,

skolemizare.

4
(c) Aplicând rezolutia pe clauzele (1) si (2), cu legarea x ← cy , obtinem clauza {Q(cy )}
, , ,

(4). Aplicând din nou rezolutia pe clauzele (3) si (4), cu legarea z ← cy , obtinem
, , ,

clauza vid .

Barem.

(a) ˆ 1p eliminarea implicatiei ,

ˆ 1p scrierea clauzei
(b) ˆ 1p eliminarea implicatiei ,

ˆ 1p introducerea negatiei în paranteze


,

ˆ 1p transformarea cuanticatorului existential în universal prin negatie


, ,

ˆ 1p eliminarea cuanticatorilor existential si universal , ,

ˆ 1p scrierea clauzei
(c) ˆ 1p prima aplicare a rezolutiei ,

ˆ 0,5p legarea lui x


ˆ 1p a doua aplicare a rezolutiei ,

ˆ 0,5p legarea lui z

8. Deniti în Prolog predicatul group(+Xs, -Yss), care primeste o list de numere nenule
, ,

Xs, si leag lista de liste Yss, obtinute prin gruparea elementelor consecutive cu acelasi
, , ,

semn. De exemplu, interogarea group([1, 2, 3, -4, -5, 6], Yss) produce legarea
Yss = [[1, 2, 3], [-4, -5], [6]].

Solut, ie. .
1 group([], []).
2 % un singur element
3 group([X], [[X]]).
4 % cel putin 2 elemente, avand acelasi semn
5 group([X,Y|Zs], [[X|Rs]|Rss]) :- X*Y > 0, !, group([Y|Zs], [Rs|Rss]).
6 % cel putin 2 elemente, avand semn diferit
7 group([X,Y|T], [[X]|Rss]) :- group([Y|T], Rss).

Barem.

ˆ 1p cazul de baz pt lista vid


ˆ 1p cazul de baz pt lista cu un element
ˆ 3p cazul cu acelasi semn, ad ugare la o list interioar existent
,

ˆ 3p cazul cu semne diferite, introducere list interioar nou


ˆ 2p diferentierea corect , a ultimelor dou cazuri, e cu cut, e cu explicitarea
conditiei pe ambele cazuri
,

9. Pornind de la o list de numere, Xs, si de la o list de liste de numere, Yss, scrieti în


, ,

Prolog o interogare care determin lista tuturor listelor din Yss care contin cel putin , ,

dou dintre elementele lui Xs. De exemplu, dac Xs = [1, 2, 3] si Yss = [[1], [1, ,

2], [1, 3], [1, 3, 4], [1, 4, 5]], rezultatul este lista [[1, 2], [1, 3], [1,
3, 4]]. Atentie! NU folositi recursivitate explicit , ci metapredicate!
, ,

5
Solut, ie. .
1 findall(Ys,
2 (member(Ys, Yss),
3 findall(X,
4 (member(X, Xs), member(X, Ys)),
5 [_,_|_])),
6 Zss)

Barem.

ˆ 2p parcurgere Yss
ˆ 2p parcurgere Xs
ˆ 2p vericare apartenent element din Xs la list individual din Yss
,

ˆ 2p conditia de minim 2 elemente


,

ˆ 2p unic satisfacere pentru întreaga list de liste, nu satisfaceri multiple pt liste


individuale

10. PROBLEMA. Se urm reste reprezentarea în Haskell a clauzelor din logica propozitional
, ,

(f r predicate, variabile sau cuanticatori), utilizând urm toarele denitii de tipuri de


,

date:
1 data Literal = Positive String
2 | Negative String
3 data Clause = Clause [Literal]

De exemplu, clauza {p, ¬q, r}, care corespunde disjunctiei p ∨ ¬q ∨ r, se poate reprezenta
,

prin Clause [Positive "p", Negative "q", Positive "r"].


(a) Instantiati clasa Show cu tipurile Literal si Clause, astfel încât aplicatia lui show
, , , ,

pe clauza din exemplul de mai sus s se evalueze la sirul "p v ~q v r". Atentie!
, ,

NU utilizati functii ca intersperse sau intercalate.


, ,

(b) Deniti functia decompose, având tipul [a] -> [(a, [a])], care primeste o list
, , ,

si elimin , pe rând, câte un singur element la un moment dat, întorcându-l în


,

pereche cu restul listei, f r acesta. De exemplu, decompose [1,2,3] produce


[(1,[2,3]),(2,[1,3]),(3,[1,2])].
(c) Pe baza functiei decompose, si a functiei deja denite complementary :: Literal
, , ,

-> Literal -> Bool, care veric dac doi literali sunt complementari (de exem-
plu, p si ¬p), scrieti functia resolve :: Clause -> Clause -> Maybe Clause,
, , ,

care rezolv dou clauze pe baza primei perechi de literali complementari, câte unul
din ecare clauz , dac aceasta exist . Hint : list comprehensions. Exemple:
ˆ resolve (Clause [Positive "p"]) (Clause [Negative "q"]) → Nothing
ˆ resolve (Clause [Positive "p"]) (Clause [Negative "p", Negative
"q"]) → Just (Clause [Negative "q"]).

Solut, ie. .
1 data Literal = Positive String | Negative String
2

3 data Clause = Clause [Literal]

6
4

5 instance Show Literal where


6 show (Positive s) = s
7 show (Negative s) = "tilda" ++ s
8

9 instance Show Clause where


10 show (Clause []) = ""
11 show (Clause (literal : literals)) = show literal ++
12 concatMap ((" v " ++) . show) literals
13

14 complementary :: Literal -> Literal -> Bool


15 complementary (Positive s1) (Negative s2) = s1 == s2
16 complementary (Negative s1) (Positive s2) = s1 == s2
17 complementary _ _ = False
18

19 decompose :: [a] -> [(a, [a])]


20 decompose [] = []
21 decompose (h : t) = (h, t) : map (\(x, xs) -> (x, h : xs))
22 (decompose t)
23

24 resolve :: Clause -> Clause -> Maybe Clause


25 resolve (Clause c1) (Clause c2) = case resolvents of
26 [] -> Nothing
27 resolvent : _ -> Just resolvent
28 where
29 d1 = decompose c1
30 d2 = decompose c2
31 resolvents = [ Clause $ c1’ ++ c2’ | (l1, c1’) <- d1,
32 (l2, c2’) <- d2,
33 complementary l1 l2 ]

De remarcat c , datorit evalu rii lenese, elementele din list


, comprehension se genereaz
doar pân la prima pereche de la literali complementari!

Barem.

(a) ˆ 1p antet Show Literal


ˆ 1p show Positive
ˆ 1p show Negative
ˆ 1p antet Show Clause
ˆ 1p show clauza vid
ˆ 5p show clauza cu cel putin un literal
,

 1p parcurgere lista de literali


 1p show pe ecare literal
 1p intercalare conector
 1p tratare particular a primului literal
 1p tip corect al rezultatului (e.g. String, nu list de String-uri)
(b) ˆ 1p caz de baz
ˆ 1p aplicatie recursiv pe restul listei
,

7
ˆ 5p ad ugarea primului element al listei în fata celei de-a doua componente a
,

ec rei perechi, cu p strarea primei componente


ˆ 3p ad ugarea unei noi perechi, cu primul element al listei drept prim compo-
nent a perechii
(c) ˆ 1p descompunere prima clauz
ˆ 1p descompunere a doua clauz
ˆ 1p iterare pe rezultele descompunerii primei clauze
ˆ 1p iterare pe rezultele descompunerii celei de-a doua clauze
ˆ 1p vericarea complementarit tii literalilor curenti
, ,

ˆ 2p constructia rezolventului
,

ˆ 1p diferentiere Just vs Nothing


,

ˆ 1p caz Nothing
ˆ 1p caz Just

8
Preciz ri:
ˆ Primele 9 subiecte au ecare câte 10p. Cele 3 cerinte ale problemei au ecare câte 10p.
,

Punctajul NU se acord în absenta justic rii r spunsului!


,

ˆ Este sucient rezolvarea a 10 itemi dintre cei 12 pentru nota maxim .


ˆ Punctajul suplimentar reprezint bonus, ce poate compensa punctajul de pe parcurs.

1. Cu ce se pot înlocui punctele din expresia de mai jos, astfel încât aceasta s se evalueze
la z ?
(λx.(x y) . . .) → z

.
Solut, ie.

Dac not m punctele cu E , obtinem (λx.(x y) E) → (E y). Rezult c E trebuie


,

s e o functie, dar, din moment ce y nu apare în rezultatul dorit, functia trebuie s e


, ,

constant , întorcându-l pe z : E ≡ λx.z .

Barem.

ˆ 3p primul pas de evaluare


ˆ 4p justicare functie constant
,

ˆ 3p forma nal

2. Fie urm toarea functie în Racket:


,

1 (define (f n x L)
2 (if (null? L)
3 L
4 (if (equal? (car L) x)
5 (if (= n 1)
6 (cdr L)
7 (cons (car L)
8 (f (sub1 n) x (cdr L))))
9 (cons (car L)
10 (f n x (cdr L))))))

(a) (5p) Ce calculeaz functia f si ce tip de recursivitate utilizeaz ?


, ,

(b) (5p) Rescrieti functia utilizând cel lalt tip de recursivitate si numiti-l.
, , , ,

Solut, ie. .
(a) Elimin a n-a aparitie a lui x din L. Utilizeaz recursivitate pe stiv .
,

(b) Recursivitate pe coad :


1 (define (f2 n x L acc)
2 (if (null? L)
3 (reverse acc)
4 (if (equal? (car L) x)
5 (if (= n 1)
6 (append (reverse acc) (cdr L))
7 (f2 (sub1 n) x (cdr L) (cons (car L) acc)))
8 (f2 n x (cdr L) (cons (car L) acc)))))

1
Barem.

(a) 5p
ˆ 3p functionalitate
,

ˆ 2p tip recursivitate
(b) 5p
ˆ 1p reverse acc
ˆ 1p append (reverse acc) (cdr L)
ˆ 1p prima aplicatie recursiv
,

ˆ 1p a doua aplicatie recursiv ,

ˆ 1p tip recursivitate
3. Deniti în Racket functia mean, care calculeaz media elementelor unei liste, utilizând
, ,

exclusiv functionale, si parcurgând lista o singur dat . Utilizati si o form de let


, , , ,

pentru evitarea calculelor duplicate.

Solut, ie. .
1 (define (mean L)
2 (let ([pair (foldl (lambda (x acc)
3 (cons (+ (car acc) x) (+ (cdr acc) 1)))
4 '(0 . 0)
5 L)])
6 (/ (car pair) (cdr pair))))

Barem.

ˆ 6p exclusiv functionale (0p pt recursivitate explicit )


,

 2p functional corect,

 1p caz de baz
 3p cazul general
ˆ 2p o singur trecere
ˆ 2p evitare calcule duplicate

4. Determinati primele 8 elemente ale uxului s din Racket:


,

1 (define s
2 (stream-cons 1
3 (stream-cons 2
4 (stream-zip-with - s naturals))))

Solut, ie. .
Sc dem naturals din s, iar dac ad ug m 1 si 2 în fata rezultatului, obtinem s:
, , ,

s0 s1 s2 s3 s4 s5 ... (stream-zip-with -)
0 1 2 3 4 5 ...
-----------------------------------
1 2 | s0-0 s1-1 s2-2 s3-3 s4-4 s5-5 ... =
s0 s1 s2 s3 s4 s5 s6 s7 ... =
1 2 1 1 -1 -2 -5 -7

2
Barem.

ˆ 6p ideea rezolv rii


 1p explicitare simbolic elemente s
 2p sc dere naturals
 1p ad ugare 1 si 2 ,

 2p corespondent elemente între s si denitia lui s


, , ,

ˆ 4p elementele (0,5p × 8)

5. Sintetizati tipul expresiei Haskell (map map).


,

Solut, ie. .
1 map :: (a -> b) -> [a] -> [b] -- prima aparitie
2 map :: (c -> d) -> [c] -> [d] -- a doua aparitie
3 a -> b = (c -> d) -> [c] -> [d]
4 a = c -> d
5 b = [c] -> [d]
6 map map :: [c -> d] -> [[c] -> [d]]

Barem.

Câte 2p pt ecare linie, cu exceptia liniei 3, optional .


, ,

6. În Haskell, tipul operatorului de compunere este (.) :: (b -> c) -> (a -> b) ->
(a -> c). Ne propunem s generaliz m acest operator, astfel încât tipul rezultatului
întors s e împachetat într-un constructor de tip unar:
1 class Composable t where
2 compose :: (b -> t c)
3 -> (a -> t b)
4 -> (a -> t c)

(a) (2p) Dac dorim s instantiem clasa de mai sus cu constructorul de tip Maybe, care
,

ar  tipul particularizat al lui compose?


(b) (8p) Scrieti instanta pentru Maybe de mai sus.
, ,

Solut, ie. .
1 instance Composable Maybe where
2 -- (b -> Maybe c) -> (a -> Maybe b) -> (a -> Maybe c)
3 compose f g a = case g a of
4 Nothing -> Nothing
5 Just b -> f b

Barem.

(a) 2p
ˆ 0,5p structura general a tipului

3
ˆ 0,5p × 3 pt ecare tip intern de functie ,

(b) 8p
ˆ 1p antet instant ,

ˆ 1p parametri compose
ˆ 1p aplicare (g a)
ˆ 1p testare rezultat (g a)
ˆ 2p caz Nothing
ˆ 2p caz Just
7. Din punct de vedere logic, propozitia ∀x.(P (x) ∨ Q(x)) NU implic propozitia ∀y.P (y) ∨
, ,

∀z.Q(z) (gânditi-v la numere naturale, P ≡ par si Q ≡ impar , i.e. faptul c orice num r
, ,

natural e e par, e impar, nu implic faptul c e toate numerele sunt pare, e c toate
sunt impare). S presupunem c încerc m totusi s o demonstr m pe a doua din prima
,

utilizând reducerea la absurd în tandem cu rezolutia. În ce punct esueaz procesul , ,

de demonstrare?

.
Solut, ie.

Propozitia ∀x.(P (x) ∨ Q(x)) are forma clauzal {P (x), Q(x)} (1). Negând presupusa
,

concluzie, obtinem: ,

¬(∀y.P (y) ∨ ∀z.Q(z))


∃y.¬P (y) ∧ ∃z.¬Q(z)
∃y.∃z.(¬P (y) ∧ ¬Q(z))
¬P (c) ∧ ¬Q(d)

Se obtin clauzele {¬P (c)} (2) si {¬Q(d)} (3), unde c si d sunt constante obtinute prin
, , , ,

skolemizare. Rezolvând clauzele (1) si (2), cu legarea x ← c, obtinem clauza {Q(c)} (4).
, ,

Din p cate, nu putem rezolva mai departe clauzele (3) si (4), din cauz c nu putem ,

unica constantele c si d. ,

Barem.

ˆ 1p clauza (1)
ˆ 1p negarea concluziei
ˆ 1p introducerea negatiei în paranteze ,

ˆ 1p transformarea cuanticatorilor universali în existentiali prin negatie , ,

ˆ 1p cele dou skolemiz ri


ˆ 1p clauzele (2) si (3) ,

ˆ 2p prima aplicare a rezolutiei, cu legarea aferent


,

ˆ 2p esecul celei de-a doua aplic ri, cu justicare


,

8. Se urm reste generarea în Prolog a combin rilor de K elemente dintr-o list de N ele-
,

mente, cu K ≤ N .
(a) (5p) Deniti predicatul extract(?Elem, +List, ?Rest), care leag parametrul
,

Elem la un element al listei List, si parametrul Rest, la lista elementelor din


,

dreapta lui. De exemplu, interogarea extract(Elem, [1, 2], List) produce


leg rile Elem = 1, List = [2], respectiv Elem = 2, List = [].

4
(b) (5p) Utilizând extract, deniti predicatul combs(+K, +List, ?Comb), care leag
,

parametrul Comb la o combinare de K elemente ale listei List. De exemplu, intero-


garea combs(2, [a, b, c], Comb) produce leg rile Comb = [a, b] ; Comb =
[a, c] ; Comb = [b, c].

Solut, ie. .
1 extract(X, [X|L], L).
2 extract(X, [_|T], R) :- extract(X, T, R).
3

4 combs(0, _, []) :- !.
5 combs(K, L, [X|C]) :- extract(X, L, M), K1 is K - 1, combs(K1, M, C).

Barem.

(a) 5p
ˆ 2p cazul cu element pe prima pozitie în list
,

 1p legare element
 1p legare rest
ˆ 3p cazul cu element în interiorul listei
 1p aplicatie recursiv
,

 1p legare element
 1p legare rest
(b) 5p
ˆ 0,5p cazul de baz
ˆ 4p cazul general
 1p aplicatie extract
,

 1p calcul nou K
 1p aplicatie recursiv
,

 1p legare combinare
ˆ 0,5p diferentiere corect între cazuri, e prin cut, e cu testare explicit a
,

conditiei K > 0 în al doilea caz


,

9. Pornind de la o list de numere, Xs, si de la o list de liste de numere, Yss, scrieti în Prolog
, ,

o interogare care determin lista tuturor listelor din Yss care e au toate elementele în
Xs, e nu au niciun element în Xs. De exemplu, dac Xs = [1, 2, 3] si Yss = [[1, ,

4], [1, 3, 2], [4]], rezultatul este lista [[1, 3, 2], [4]]. Atentie! NU folositi
, ,

recursivitate explicit , ci metapredicate!

Solut, ie. .
1 findall( Ys,
2 ( member(Ys, Yss),
3 ( forall(member(Y, Ys), member(Y, Xs));
4 forall(member(Y, Ys), \+ member(Y, Xs))
5 )
6 ),
7 Zss
8 ).

5
Barem.

ˆ 2p enumerare liste din Yss


ˆ 3p conditia apartenentei tuturor elementelor unei liste individuale la Xs
, ,

ˆ 3p conditia neapartenentei vreunui element al unei liste individuale la Xs


, ,

ˆ 1p între member si forall-uri


s
,i ,

ˆ 1p sau între forall-uri

0p pt recursivitate explicit
10. PROBLEMA. Se urm reste reprezentarea în Haskell a unor propozitii (restrictionate)
, , ,

din logica cu predicate de ordinul I, utilizând urm toarele denitii de tipuri de date: ,

1 data Term = Const String | Var String


2 deriving Eq
3 data Sentence = Atom String [Term]
4 type Substitution = [(String, Term)]

Astfel, un termen este o constant sau o variabil , iar o propozitie este un atom (aplicatia , ,

unui predicat pe niste termeni). De exemplu, utilizând notatia din Prolog, atomul p(X, c),
, ,

care corespunde aplicatiei predicatului p asupra variabilei X si constantei c, se poate repre-


, ,

zenta prin Atom "p" [Var "X", Const "c"]. O substitutie este o multime de leg ri de , ,

nume de variabile la termeni. De exemplu, substitutia {X ← Y, Y ← c} se poate repre- ,

zenta ca subst = [("X", Var "Y"), ("Y", Const "c")]. Pentru punctaj maxim,
toate functiile de mai jos trebuie implementate f r recursivitate explicit .
,

(a) Deniti functia lookup :: Substitution -> Term -> Term, care caut o vari-
, ,

abil într-o subtitutie si întoarce termenul aferent din pereche, dac ea este legat .
, ,

Pentru variabile nelegate sau pentru constante, functia întoarce termenul primit ca ,

parametru. De asemenea, deniti functia lookupIterate :: Substitution -> , ,

Term -> [Term], care aplic la innit functia lookup pe rezultatul c ut rii ante- ,

rioare.
(b) Deniti functia lookupEnd :: Substitution -> Term -> Term, care întoarce
, ,

ultimul element diferit de anterioarele din lista întoars de lookupIterate.


(c) Deniti functia unifyTerms :: Substitution -> Term -> Term -> Bool, care
, ,

veric dac doi termeni unic , în baza substitutiei primite ca parametru. De ,

asemenea, deniti functia unifySentences :: Substitution -> Sentence ->


, ,

Sentence -> Bool, care veric dac doi atomi unic în baza substitutiei. Tre- ,

buie s vericati numele atomilor si termenii de pe aceleasi pozitii.


, , , ,

Exemple:
ˆ lookup subst (Var "X") → Var "Y"
ˆ lookup subst (Var "Z") → Var "Z"
ˆ lookup subst (Const "c") → Const "c"
ˆ take 4 $ lookupIterate subst (Var "X") → [Var "X", Var "Y", Const "c",
Const "c"]
ˆ lookupEnd subst (Var "X") → Const "c"
ˆ unifyTerms subst (Var "X") (Var "Y") → True

6
ˆ unifyTerms subst (Var "X") (Const "c") → True
ˆ unifyTerms subst (Var "X") (Const "d") → False
ˆ unifyTerms subst (Const "c") (Const "d") → False
ˆ unifySentences subst (Atom "p" [Var "X", Const "c"]) (Atom "p" [Var
"Y", Var "X"]) → True
ˆ unifySentences subst (Atom "p" [Var "X", Const "c"]) (Atom "u" [Var
"Y", Var "X"]) → False
ˆ unifySentences subst (Atom "p" [Var "X", Const "c"]) (Atom "p" [Var
"Y", Const "d"]) → False

Solut, ie. .
1 data Term
2 = Const String
3 | Var String
4 deriving (Eq, Show)
5

6 data Sentence = Atom String [Term]


7 deriving Show
8

9 type Substitution = [(String, Term)]


10

11 subst :: Substitution
12 subst = [("X", Var "Y"), ("Y", Const "c")]
13

14 lookup :: Substitution -> Term -> Term


15 lookup subst var@(Var name) = foldr f var subst
16 where
17 f (name’, term) found = if name == name’ then term else found
18 lookup _ term = term
19

20 lookupIterate :: Substitution -> Term -> [Term]


21 lookupIterate subst term = iterate (lookup subst) term
22

23 lookupEnd :: Substitution -> Term -> Term


24 lookupEnd subst term = fst $ head $ filter (\(t1, t2) -> t1 == t2) $
25 zip result $ tail result
26 where
27 result = lookupIterate subst term
28

29 unifyTerms :: Substitution -> Term -> Term -> Bool


30 unifyTerms subst term1 term2 =
31 lookupEnd subst term1 == lookupEnd subst term2
32

33 unifySentences :: Substitution -> Sentence -> Sentence -> Bool


34 unifySentences subst (Atom name1 terms1) (Atom name2 terms2) =
35 name1 == name2 && length terms1 == length terms2 &&
36 and (zipWith (unifyTerms subst) terms1 terms2)

7
Barem.

(a) ˆ 8p lookup
 3p absenta recusivit tii explicite
, ,

 1p parcurgere substitutie,

 1p localizare variabil legat


 1p extragere termen aferent
 1p caz variabil nelegat
 1p caz constant
ˆ 2p lookupIterate
 2p absenta recusivit tii explicite
, ,

(b) ˆ 3p absenta recursivit tii explicite


, ,

ˆ 2p parcurgere rezultat lookupIterate


ˆ 3p determinare ultim element diferit
ˆ 2p întoarcere element
(c) ˆ 2p unifyTerms
 1p aplicare lookupEnd pe cei doi termeni
 1p vericare egalitate
ˆ 8p unifySentences
 3p absenta recusivit tii explicite
, ,

 1p vericare nume atomi


 2p aplicare unifyTerms pe ecare pereche de termeni
 2p îmbinare rezultate unicare pentru toti termenii
,

8
Examen PP varianta A — NOT EXAM MODE
31.05.2019
atent, ie: Avet, i 2 ore · 1-9: 10p; 10: 30p · 100p pentru nota maximă · Justificat, i răspunsurile!

1. Reducet, i expresia lambda E = (λx.(x (λy.z x)) λx.x)


Solut, ie:

− (λx.(x z) λx.x) → − (λx.x z) →
− z

2. Se dă următorul cod Racket:


(define computation (delay (+ 5 5)))
(* 5 5)
(define (f x) (cons x (force computation)))
(map f ’(1 2 3 4))
(a) De câte ori se realizează adunarea?
(b) Prima evaluare a adunării se realizează ı̂nainte sau după ı̂nmult, ire?
(c) Rescriet, i codul pentru computation s, i pentru f folosind ı̂nchideri funct, ionale ı̂n loc de promisiuni
s, i răspundet, i din nou la ı̂ntrebările de la (a) s, i (b).
Solut, ie:
(a) o singură dată, la prima evaluare a lui computation.
(b) după ı̂nmult, ire, atunci când se apelează prima oară (force computation)
(c) (define computation (λ () (+ 5 5)))
(* 5 5)
(define (f x) (cons x (computation)))
(map f ’(1 2 3 4))
acum se apelează de 4 ori, la fiecare evaluare a lui computation; dar prima dată tot după
ı̂nmult, ire.

3. Date fiind două liste de numere L1 s, i L2, scriet, i ı̂n Racket codul care produce o listă de perechi (x .
n), unde x este un element din L1, iar n este numărul de aparit, ii ale lui x ı̂n L2. E.g. pentru L1 =
(1 4 5 3) s, i L2 = (1 3 2 4 1 5 3 9) rezultatul este ((1 . 2) (4 . 1) (5 . 1) (3 . 2)). Nu
folosit, i recursivitate explicită.
Solut, ie:
(map (lambda (x) (cons x (length (filter ((curry equal?) x) L)))) ’(1 4 5 3))
sau
(map (lambda (x) (cons x (length (filter (lambda (y) (equal? x y)) L)))) ’(1 4 5 3))

4. Sintetizat, i tipul următoarei funct, ii ı̂n Haskell: f x y = x y (y x)


Solut, ie:
x :: a -> b -> c
y :: a , dar s, i y :: (a -> b -> c) -> b ⇒ a = (a -> b -> c) -> b Eroare, deoarece a nu poate uni-
fica cu o expresie de tip care ı̂l cont, ine strict pe a.

5. (a) Cât, i pas, i de concatenare sunt realizat, i pentru evaluarea expresiei Racket
(car (append ’(1 2) ’(3 4))) ?
(b) Dar pentru expresia Haskell head $ [1, 2] ++ [3, 4] ?
Solut, ie:
(a) Se concatenează ı̂ntregime listele, deci doi pas, i.
(b) Este suficient un singur pas pentru ca head să ı̂ntoarcă primul element.

6. Evident, iat, i o posibilă instant, ă a clasei Haskell de mai jos:


class MyClass c where
f :: c a -> a
Solut, ie:
instance MyClass [] where
f = head

7. Transformat, i propozit, ia ,,Nu tot ce zboară se mănâncă.” ı̂n logică cu predicate de ordinul ı̂ntâi.
Solut, ie:
∃x.zboara(x) ∧ ¬se mananca(x) – există s, i lucruri care zboară s, i nu se mănâncă
sau
¬(∀x.zboara(x) ⇒ se mananca(x)) – nu este adevărat că orice care zboară automat se s, i mănâncă
8. Se dă programul Prolog:
p(R, S) :- member(X, R),
findall(Y, (member(Y, R), Y \= X), T), !, q(X, T, S).
q(X, A, [X|A]). q(X, [A|B], [A|C]) :- q(X, B, C).
Dacă predicatul p primes, te ı̂n primul argument o listă, la ce valori leagă al doilea argument? Câte
solut, ii are interogarea p([1, 2, 3, 4], S) ?
Solut, ie:
Ia primul element (s, i elimină duplicatele lui) s, i ı̂l pune pe diverse pozit, ii ale listei, inclusiv pe prima.
Patru solut, ii: [1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4], [2, 3, 4, 1]
9. Se dau următoarele relat, ii genealogice prin predicatul c(Parinte, Copil). Implementat, i predicatul
frati(X, F), care leagă F la lista de frat, i ai lui X (dacă există). De exemplu, pentru definit, iile de
mai jos, interogarea frati(herodot, F) leagă F la [faramir, george].
c(alex, celia). c(alex, delia). c(alex, marcel).
c(barbra, celia). c(barbra, delia). c(barbra, marcel).
c(delia, faramir). c(delia, george). c(delia, herodot).
c(erus, faramir). c(erus, george). c(erus, herodot).
Solut, ie:
frati(X, F) :- c(P, X), !, findall(Y, (c(P, Y), Y \= X), F). sau
frati(X, F) :- findall(Y, (c(P, X), c(P, Y), Y \= X), F1), sort(F1, F).

10. PROBLEMA (Poate fi implementată ı̂n orice limbaj studiat la PP.) Se urmăres, te implementarea
unui multi-map, care este un tabel asociativ ı̂n care unei chei i se pot asocia oricâte valori.
(a) Descriet, i reprezentarea multi-map-ului. Pentru Haskell, dat, i definit, ia tipului de date polimorfic.
Definit, i funct, ia/predicatul lookup’, care extrage lista tuturor valorilor asociate cu o cheie.
(b) Definit, i funct, ia/predicatul insert’, pentru adăugarea unei noi asocieri ı̂ntre o cheie s, i o valoare.
(c) Definit, i funct, ia/predicatul map’, care aplică o funct, ie/predicat pe fiecare valoare din multi-map.
Notă: ı̂n Prolog, map’ va aplica ı̂ntotdeauna un acelas, i predicat p(+VIn, -VOut).
Solut, ie:
Racket:
(define multimapExample ’((a 1 2 3) (b 5 6 7) (c 7 8)))

(define (lookup k m) (cdr (assoc k m)))


(lookup ’b multimapExample)

(define (insert k v m)
(let-values (((bef aft) (splitf-at m (λ(kv) (not (equal? (car kv) k))))))
(if (null? aft)
(cons (list k v) m)
(append bef (list (cons k (cons v (cdar aft)))) (cdr aft))
)))
(insert ’d 5 multimapExample)
(insert ’b 9 multimapExample)
(define (mmap f m)
(map
(lambda (kv) (cons (car kv) (map f (cdr kv))))
m))
(mmap add1 multimapExample)
Haskell:
data MultiMap k a = MM [(k, [a])] deriving (Eq, Show)

ins :: Eq k => k -> a -> MultiMap k a -> MultiMap k a


ins k a (MM lst) = MM $ case back of
[] -> (k, [a]) : front
( , as) : back -> (k, a : as) : front ++ back
where
(front, back) = break ((== k) . fst) lst

map’ :: (a -> b) -> MultiMap k a -> MultiMap k b


map’ f (MM lst) = MM $ map (\(k, as) -> (k, map f as)) lst

test = ins 2 ’z’ $ ins 3 ’c’ $ ins 2 ’b’ $ ins 1 ’a’ $ MM []


Prolog:
lookup(K, MM, Values) :- member((K, Values), MM).

insert(K, V, MM, Out) :-


select((K, L), MM, MM1), !,
Out = [(K, [V|L]) | MM1].
insert(K, V, MM, [(K, [V]) | MM]).

f(V, V1) :- V1 is V + 1.

map(MM, Out) :-
findall((K, L1),
( member((K, L), MM),
findall(E1, (member(E, L), f(E, E1)), L1)),
Out).
Examen PP varianta B — NOT EXAM MODE
31.05.2019
atent, ie: Avet, i 2 ore · 1-9: 10p; 10: 30p · 100p pentru nota maximă · Justificat, i răspunsurile!

1. Reducet, i expresia lambda E = (y (λx.λx.x (λy.y y)))


Solut, ie:

− (y (λx.λx.x y)) → − (y λx.x)
sau

− (y λx.x)
2. Se dă următorul cod Racket:
(define computation (λ () (equal? 5 5)))
(define (f x) (and (> x 5) (computation)))
(filter f ’(1 3 5 7 9))
(a) De câte ori se apelează funct, ia equal? ?
(b) Rescriet, i codul pentru computation s, i pentru f folosind promisiuni (pentru ı̂ntârzierea lui
computation) s, i răspundet, i din nou la ı̂ntrebarea (a).
Solut, ie:
(a) de 2 ori (pentru fiecare element mai mare decât 5)
(b) (define computation (delay (equal? 5 5)))
(define (f x) (and (> x 5) (force computation)))
(filter f ’(1 3 5 7 9))
acum se apelează o singură dată, la prima evaluare a lui computation.

3. Dată fiind o listă de liste de numere LL, scriet, i ı̂n Racket codul care produce sublista lui LL ı̂n care
pentru toate elementele L suma elementelor este cel put, in egală cu produsul lor. E.g. pentru L = ((1
2 3) (1 2) (4 5) (.5 .5)) rezultatul este ((1 2 3) (1 2) (0.5 0.5)). Nu folosit, i recursivitate
explicită.
Solut, ie:
(filter (lambda (L) (>= (apply + L) (apply * L))) ’((1 2 3) (1 2) (4 5) (.5 .5)))

4. Sintetizat, i tipul următoarei funct, ii ı̂n Haskell: f = map (++)


Solut, ie:
map :: (a -> b) -> [a] -> [b]
(++) :: [c] -> ([c] -> [c])
a = [c]
b = [c] -> [c]
f :: [[c]] -> [[c] -> [c]]

5. (a) Câte aplicat, ii ale funct, iei de incrementare sunt calculate pentru evaluarea expresiei Racket
(length (map add1 ’(1 2 3 4 5 6 7 8 9 10))) ?
(b) Dar pentru expresia Haskell length $ map (+ 1) [1 .. 10] ?
Solut, ie:
(a) Toate elementele listei sunt evaluate, deci 10.
(b) Elementele listei nu sunt evaluate, deci 0.

6. Supraı̂ncărcat, i ı̂n Haskell operatorii (+) s, i (*) pentru valori booleene, pentru a surprinde operat, iile
de sau, respectiv s, i logic.
Solut, ie:
instance Num Bool where
(+) = (||)
(*) = (&&)
7. Transformat, i propozit, ia ,,Nu mor caii când vor câinii.” ı̂n logică cu predicate de ordinul ı̂ntâi, folosind
predicatele caii mor(c^and) s, i c^ainii vor(c^and).
Solut, ie:
∃t.cainii vor(t) ∧ ¬caii mor(t) – există s, i momente când câinii vor dar caii nu mor
sau
¬(∀t.cainii vor(t) ⇒ caii mor(t)) – nu este adevărat că oricând câinii vor automat caii mor
8. Se dă programul Prolog:
p( , [], []).
p(A, [A|B], B) :- !.
p(A, [B|C], [B|D]) :- p(A, C, D). Ce relat, ie există ı̂ntre cele 3 valori X, Y, Z, dacă p(X, Y, Z)
este adevărat?
Solut, ie:
Este predicatul select, iar dacă primul argument este nelegat face select la primul element. Pre-
dicatul select(X, Y, Z) este adevărat dacă X este un element din lista Y, iar Z este exact lista Y, ı̂n
afară de elementul X.
9. Se dau următoarele relat, ii genealogice prin predicatul c(Parinte, Copil). Implementat, i predicatul
veri(X, V), care leagă V la lista de veri ai lui X (dacă există). De exemplu, pentru definit, iile de mai
jos, interogarea veri(faramir, V) leagă V la [jenny, karl, ninel, octav].
c(alex, celia). c(alex, delia). c(alex, marcel).
c(barbra, celia). c(barbra, delia). c(barbra, marcel).
c(delia, faramir).
c(ion, jenny). c(ion, karl). c(celia, jenny). c(celia, karl).
c(marcel, ninel). c(marcel, octav).
Solut, ie:
veri(X, L) :- setof(V, P∧ B∧ U∧ (c(P, X), c(B, P), c(B, U), U\=P, c(U, V)), L). sau
veri(X, L) :- findall(V, (c(P, X), c(B, P), c(B, U), U\=P, c(U, V)), L1), sort(L1, L).

10. PROBLEMA (Poate fi implementată ı̂n orice limbaj studiat la PP.) Se urmăres, te implementarea
unui hash set, care reprezintă o mult, ime grupând valorile ı̂n bucket-uri, fiecare bucket fiind unic
determinat de hash-ul valorilor din bucket (toate valorile din bucket au acelas, i hash). Hashul unei
valori va fi dat de funct, ia hash, respectiv predicatul hash(+V, -Hash).
(a) Descriet, i reprezentarea hash set-ului. Pentru Haskell, dat, i definit, ia tipului de date polimorfic.
Definit, i funct, ia/predicatul values’, care extrage lista tuturor valorilor asociate cu un hash.
(b) Definit, i funct, ia/predicatul insert’, pentru adăugarea unei valori.
(c) Definit, i funct, ia/predicatul map’, care aplică o funct, ie/predicat pe fiecare valoare din hash-set.
Notă: ı̂n Prolog, map’ va aplica ı̂ntotdeauna un acelas, i predicat p(+VIn, -VOut).
Solut, ie:
Racket:
(define (hash int) (remainder int 2))
(define hashExample ’((1 3 5 7) (0 0 2 8)))

(define (lookup hash m) (cdr (assoc hash m)))


(lookup 1 hashExample)

(define (insert v m)
(let ((k (hash v)))
(let-values (((bef aft) (splitf-at m (λ(kv) (not (equal? (car kv) k))))))
(if (null? aft)
(cons (list k v) m)
(append bef (list (cons k (cons v (cdar aft)))) (cdr aft))
))))
(insert 9 hashExample)
(insert 9 (cdr hashExample))

(define (mmap f m)
(map
(λ(kv) (cons (car kv) (map f (cdr kv))))
m))
(mmap add1 hashExample)
Haskell:
class Hashable a where hash :: a -> Int -- nu a fost cerut ^ın rezolvare
instance Hashable Int where hash x = mod x 2 -- nu a fost cerut ^
ın rezolvare

data HashSet a = HS [(Int, [a])] deriving (Eq, Show)

--ins :: Hashable a => a -> HashSet a -> HashSet a


--// tipul corect, dar mai greu de testat
ins :: Int -> HashSet Int -> HashSet Int
ins a (HS lst) = HS $ case back of
[] -> (k, [a]) : front
( , as) : back -> (k, a : as) : front ++ back
where
k = hash a
(front, back) = break ((== k) . fst) lst

map :: (Hashable a, Hashable b) => (a -> b) -> HashSet a -> HashSet b


map f (HS lst) = HS $ map (\(k, as) -> (k, map f as)) lst

test2 = ins 5 $ ins 2 $ ins 4 $ ins 8 $ HS []


test3 = map (+1) $ test2
Prolog:
hash(V, Hash) :- Hash is mod(V, 2).

lookup(Hash, HS, Values) :- member((Hash, Values), HS).

insert(V, HS, Out) :-


hash(V, K),
select((K, L), HS, HS1), !,
Out = [(K, [V|L]) | HS1].
insert(V, HS, [(K, [V]) | HS]) :- hash(V, K).

% insert(5, [], H1), insert(7, H1, H2), insert(2, H2, H3).

f(V, V1) :- V1 is V + 1.

map(HS, Out) :-
findall((K, L1),
( member((K, L), HS),
findall(E1, (member(E, L), f(E, E1)), L1)),
Out).
Varianta A – Examen PP (2020)

1. (λx y z.corp a b c) este forma prescurtată de a scrie (((λx.λy.λz.corp a) b) c).


Fie următoarele definit, ii ı̂n Calcul Lambda:
true = λx y.x
false = λx y.y
s, i fie operatorul logic:
op = λx y.(x y true)
Descriet, i pas cu pas (nu efectuat, i mai multe β-reduceri deodată) comportamentul lui op pe
valori de tip true sau/s, i false, astfel ı̂ncât să putet, i conchide la final că op se comportă ca
unul dintre operatorii logici cunoscut, i (s, i precizat, i care este operatorul a cărui identitate a
fost ascunsă prin folosirea numelui ”op”).
Solut, ie:
(op true y) = (λx y.(x y true) true y) → (true y true) = (λx y.x y true) → y
(op false y) = (λx y.(x y true) false y) → (false y true) = (λx y.y y true) → true
Concluzie: op este ⇒

2. Ce tip de recursivitate are implementarea de mai jos?

(define (z x y)
(if (or (null? x) (null? y))
'()
(cons (car x) (cons (car y) (z (cdr y) (cdr x))))))

Definit, i (tot ı̂n Racket) cât mai eficient funct, ia zz care are acelas, i efect cu z, dar foloses, te un
alt tip de recursivitate.
Justificat, i pe scurt eficient, a solut, iei voastre.
Solut, ie:
Recursivitate pe stivă – o vom transforma ı̂n recursivitate pe coadă.

(define (zz x y)
(let z ((x x) (y y) (r '()))
(if (or (null? x) (null? y))
(reverse r)
(z (cdr y) (cdr x) (cons (car y) (cons (car x) r))))))

Eficient, ă:

• recursivitatea pe coadă elimină nevoia de a ocupa spat, iu pe stiva de apeluri recursive;


• adăugarea cu cons ı̂n acumulatorul r (s, i inversarea lui r la final) duce la o construct, ie
ı̂n timp liniar, spre deosebire de timp pătratic dacă făceam append la finalul lui r.

3. Atent, ie: Acest exercit, iu nu se punctează decât dacă este rezolvat fără a folosi recursivitate
explicită (deci nici letrec sau named let). Solut, iile explicit recursive se punctează cu 0.
Se dă o listă L care cont, ine numere s, i/sau liste de numere (maxim un nivel de imbricare).
Să se definească, ı̂n Racket, funct, ia two-sums care ı̂ntoarce o pereche de valori, reprezentând
suma numerelor la ”adâncime 0” din L, respectiv suma numerelor la ”adâncime 1” din L.
ex: (two-sums ’(1 2 (3 4) 5 (6) 7)) va ı̂ntoarce ’(15 . 13), pentru că 1 + 2 + 5 + 7 =
15, iar 3 + 4 + 6 = 13
Solut, ie:

(define (two-sums L)
(let ((outside (filter (compose not list?) L))
(inside (apply append (filter list? L))))
(cons (apply + outside) (apply + inside))))

4. Cum decurge, pas cu pas, evaluarea expresiei:


head $ ([1] ++ [2]) ++ [3]

presupunând cunoscută implementarea operatorului de concatenare:

[] ++ ys = ys
(x : xs) ++ ys = x : (xs ++ ys)

Solut, ie:

head $ ([1] ++ [2]) ++ [3] -- (1)


head $ (1 : ([] ++ [2])) ++ [3] -- (2)
head $ 1 : (([] ++ [2]) ++ [3]) -- (3)
1 -- (4)

5. Sintetizat, i ı̂n Haskell, pas cu pas, tipul funct, iei g:

g fs xs = zipWith (\f x -> f x) fs xs

cunoscând tipul funct, iei zipWith:

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

Solut, ie:

f :: a = d -> e
x :: b = d
c = e
fs :: [a] = [d -> e]
xs :: [b] = [d]
g :: [d -> e] -> [d] -> [e]

6. Supraı̂ncărcat, i ı̂n Haskell operatorul de egalitate pentru funct, ii unare cu parametru numeric,
astfel ı̂ncât două funct, ii să fie considerate egale dacă valorile lor coincid ı̂n cel put, in 10 puncte
din intervalul 1, . . . , 100.
Solut, ie:

instance (Num a, Enum a, Eq b) => Eq (a -> b) where


f == g = length (filter id [f x == g x | x <- [1..100]]) >= 10

7. Folosit, i pentru scriere NOT(), AND s, i OR ca operatori logici s, i ORICARE s, i EXISTĂ drept cuantifi-
catori. Scriet, i cu caps ca să nu existe confuzii cu limbajul obis, nuit. Putet, i prescurta numele
predicatelor.
S, tiind că ”Cine ı̂mparte (din ceva), parte ı̂s, i face (din acel ceva).”, s, i că
ıs, i face parte din(marcel, tort)), dorim să demonstrăm că NOT(^
NOT(^ ımparte(marcel,
tort)).

a) Traducet, i proverbul ı̂n LPOI (FOL) utilizând numai predicatele ment, ionate ı̂n problemă.
b) Demonstrat, i prin metoda rezolut, iei concluzia cerută.

Nu se punctează demonstrat, iile care nu folosesc metoda rezolut, iei.


Solut, ie:

a) ∀X.∀Y.^
ımparte(X, Y) ⇒ ı
^s, i face parte(X, Y)
b) Clauzele:
(1) ¬^ımparte(X, Y) ∨ ı ^s, i face parte din(X, Y)
(2) ı
^mparte(marcel, tort)
(3) ¬^ıs, i face parte din(marcel, tort)
(1) + (2) {X ← marcel} → ı ^s, i face parte din(marcel, tort) (4)
(3) + (4) →  (clauza vidă)
8. Implementat, i predicatul filtersorted(+LLIn, -LLOut), care primes, te ı̂n LLIn o listă de
liste de numere s, i pune ı̂n LLOut doar acele liste de numere care sunt sortate crescător.
De exemplu, filtersorted([[2, 1], [1, 3], [1, 2, 3, 4], [1, 2, 4, 2]], X) leagă
X la [[1, 3], [1, 2, 3, 4]]. Explicat, i cum funct, ionează implementarea.
Solut, ie:

filtersorted([], []).
filtersorted([E|LLIn], [E|LLOut]) :- sort(E, E), !, filtersorted(LLIn, LLOut).
filtersorted([_|LLIn], LLOut) :- filtersorted(LLIn, LLOut).

9. Folosit, i unul sau mai multe dintre predicatele findall, forall, bagof, setof pentru a im-
plementa predicatul med(+L, -Med), care găses, te elementul median al listei, cu proprietatea
că numărul de elemente mai mari decât Med este diferit cu cel mult 1 de numărul de elemente
mai mici decât Med). Nu utilizat, i recursivitate explicită. Explicat, i cum funct, ionează solut, ia.
Solut, ie:

med(L, Med) :- member(Med, L), findall(X, (member(X, L), X < Med), L1),
findall(X, (member(X, L), X > Med), L2), length(L1, LL1),
length(L2, LL2), abs(LL1-LL2) =< 1.

10. În acest exercit, iu urmărim crearea unui index de cuvinte dintr-un text: pentru fiecare cuvânt,
vom avea o listă cu liniile din text pe care acest apare. În acest scop definim următoarele
alias-uri de tip.
Utilizat, i următorul schelet:

import Data.List
import Data.Char
import Data.Maybe

type Text = String


-- pereche ^
ıntre un cuv^
ant s, i o linie din text
type Pair = (String, Int)
-- pereche ^
ıntre un cuv^
ant s, i lista tuturor liniilor pe care acesta apare
type Index = [(String, [Int])]

• (10p) Un text constă ı̂ntr-un string de cuvinte care pot fi separate prin spat, ii sau enter
(caracterul \n). De asemenea, ı̂n text pot apărea semne de punctuat, ie precum virgulă,
punct, semnul ı̂ntrebării sau semnul exclamării (după care apare minim un spat, iu sau
enter).
Să se implementeze funct, ia textToLines care primes, te un text s, i ı̂ntoarce o listă de
perechi de forma: (linie, text aflat pe acea linie procesat).
Un text procesat reprezintă textul din care s-au eliminat semnele de punctuat, ie iar
literele mari au fost transformate ı̂n litere mici.
{-
Utile:
-- ^
ımparte un text ^ın linii, pars^and după '\n'
lines :: String -> [String]
-- transformă o literă mare ^
ın literă mică
toLower :: Char -> Char

ex:
*Main> textToLines "A venit,\na venit\ntoamna!"
[(1,"a venit"),(2,"a venit"),(3,"toamna")]
-}

textToLines :: Text -> [(Int, Text)]


textToLines = undefined
• (10p) Să se implementeze funct, ia insPair care inserează o pereche (cuv^
ant, linie)
ı̂ntr-un index, fără a crea linii duplicate.
{-
ex:
*Main> insPair ("toamna",2) [("a", [1,2])]
[("toamna",[2]),("a",[1,2])]
*Main> insPair ("toamna",2) [("a", [1,2]), ("toamna", [1])]
[("toamna",[2,1]),("a",[1,2])]
*Main> insPair ("toamna",2) [("a", [1,2]), ("toamna", [1,2])]
[("a",[1,2]),("toamna",[1,2])] -- nu s-a mai adăugat ^
ıncă o dată linia
,→ 2
-}

insPair :: Pair -> Index -> Index


insPair = undefined
• (10p) Să se implementeze funct, iile allPairs s, i textToIndex. allPairs primes, te un text
s, i creează toate perechile (cuv^ant, linie) pentru acel text. textToIndex primes, te un
text s, i creează indexul (sortat alfabetic după cuvânt) pentru acel text.
{-
Utile: words :: String -> [String] -- ^
ımparte un text ^ın
,→ cuvinte, pars^
and după spat, ii
Obs: tuplurile sunt ordonabile ^ ın Haskell (după primul element din
,→ tuplu, apoi al doilea, etc).

ex:
*Main> allPairs "A venit,\na venit\ntoamna!"
[("a",1),("venit",1),("a",2),("venit",2),("toamna",3)]
*Main> textToIndex "A venit,\na venit\ntoamna!"
[("a",[1,2]),("toamna",[3]),("venit",[1,2])]
-}
allPairs :: Text -> [Pair]
allPairs = undefined

textToIndex :: Text -> Index


textToIndex = undefined

Solut, ie:

-1.
textToLines :: Text -> [(Int, Text)]
textToLines = zip [1 ..] . lines . map toLower . filter (`notElem` ",.?!")

--2.
insPair :: Pair -> Index -> Index
insPair (word, line) index = case lookup word index of
Nothing -> (word, [line]) : index
Just lines -> if elem line lines then index
else (word, line : lines) : filter ((/=
,→ word) . fst) index

--3.
allPairs :: Text -> [Pair]
allPairs txt = [ (word, line) | (line, text) <- textToLines txt, word <-
,→ words text ]

textToIndex :: Text -> Index


textToIndex = sort . foldr insPair [] . allPairs
Varianta B – Examen PP (2020)

1. (λx y z.corp a b c) este forma prescurtată de a scrie (((λx.λy.λz.corp a) b) c).


Fie următoarele definit, ii ı̂n Calcul Lambda:
true = λx y.x
false = λx y.y
s, i fie operatorul logic:
op = λx y.(x true y)
Descriet, i pas cu pas (nu efectuat, i mai multe β-reduceri deodată) comportamentul lui op pe
valori de tip true sau/s, i false, astfel ı̂ncât să putet, i conchide la final că op se comportă ca
unul dintre operatorii logici cunoscut, i (s, i precizat, i care este operatorul a cărui identitate a
fost ascunsă prin folosirea numelui ”op”).
Solut, ie:
(op true y) = (λx y.(x true y) true y) → (true true y) = (λx y.x true y) → true
(op false y) = (λx y.(x true y) false y) → (false true y) = (λx y.y true y) → y
Concluzie: op este or
2. Ce tip de recursivitate are implementarea de mai jos?
(define (f x)
(let h ((x x) (r '()))
(cond ((null? x) (reverse r))
((list? (car x)) (h (append (car x) (car x) (cdr x)) r))
(else (h (cdr x) (cons (car x) r))))))
Definit, i (tot ı̂n Racket), eliminând nevoia folosirii unui helper, funct, ia ff care are acelas, i efect
cu f, dar foloses, te un alt tip de recursivitate.
Precizat, i pe scurt care modificare din cod a dus la modificarea tipului de recursivitate.
Solut, ie:
Recursivitate pe coadă – o vom transforma ı̂n recursivitate pe stivă.
(define (ff x)
(cond ((null? x) x)
((list? (car x)) (ff (append (car x) (car x) (cdr x)))) ;; nicio
,→ modificare
(else (cons (car x) (ff (cdr x)))))) ;; acest apel face ca
,→ recursivitatea să devină pe stivă
3. Atent, ie: Acest exercit, iu nu se punctează decât dacă este rezolvat fără a folosi recursivitate
explicită (deci nici letrec sau named let). Solut, iile explicit recursive se punctează cu 0.
Se dă o listă L care cont, ine numere s, i/sau liste de numere s, i/sau liste de numere (maxim 2
niveluri de imbricare). Să se definească, ı̂n Racket, funct, ia count-nulls care numără listele
vide aflate pe orice nivel de imbricare.
ex: (count-nulls ’(() 1 (2 () (3 4 5)) (()))) va ı̂ntoarce 3, pentru că avem o listă
vidă direct ı̂n L, s, i ı̂ncă 2 ı̂n liste interioare lui L (acesta fiind s, i nivelul maxim de imbricare
admis, deci nu trebuie căutate liste vide la adâncime mai mare, de exemplu ’(0 (1 (2 ())))
nu este un input valid pentru problemă).
Solut, ie:
(define (count-nulls L)
(let* ((surface-nulls (length (filter null? L)))
(inner-lists (filter list? L))
(all-inner (apply append inner-lists))
(deep-nulls (length (filter null? all-inner))))
(+ surface-nulls deep-nulls)))
4. Definit, i ı̂n Racket următorul flux:
'((1) (2 1 2) (3 2 1 2 3) (4 3 2 1 2 3 4) (5 4 3 2 1 2 3 4 5) ...)

Solut, ie:

(define my-stream
(stream-cons '(1)
(stream-map (lambda (L) (let ([new (list (add1 (car L)))])
(append new L new)))
my-stream)))

5. Sintetizat, i ı̂n Haskell, pas cu pas, tipul funct, iei g:

g f (x, y) = x ++ map f y

Solut, ie:

g :: a -> b -> c
f :: a = d -> e
(x, y) :: b = (i, j)
(++) :: [k] -> [k] -> [k]
map :: (m -> n) -> [m] -> [n]
i = [k]
d -> e = m -> n
d = m
e = n
j = [m]
[n] = [k]
n = k
c = [k]
g :: (m -> k) -> ([k], [m]) -> [k]

6. Scriet, i o instant, ă posibilă a clasei de mai jos, cont, inând o implementare neconstantă a
funct, iei f.

class MyClass c where


f :: Ord a => c a -> c a -> c Bool

Solut, ie:

instance MyClass Maybe where


f (Just x) (Just y) = Just $ x <= y
f _ _ = Nothing

7. Folosit, i pentru scriere NOT(), AND s, i OR ca operatori logici s, i ORICARE s, i EXISTĂ drept cuantifi-
catori. Scriet, i cu caps ca să nu existe confuzii cu limbajul obis, nuit. Putet, i prescurta numele
predicatelor.
S, tiind că ”Capul plecat, sabia nu ı̂l taie”, s, i că i se taie capul lui(marcel), dorim să
demonstrăm că NOT(are capul plecat(marcel)).

a) Traducet, i proverbul ı̂n LPOI (FOL) utilizând numai predicatele ment, ionate ı̂n problemă.
b) Demonstrat, i prin metoda rezolut, iei concluzia cerută.

Nu se punctează demonstrat, iile care nu folosesc metoda rezolut, iei.


Solut, ie:

a) ∀X.are capul plecat(X) ⇒ ¬i se taie capul lui(X)


b) Clauzele:
(1) ¬are capul plecat(X) ∨ ¬i se taie capul lui(X)
(2) are capul plecat(marcel)
(3) i se taie capul lui(marcel)
(1) + (2) {X ← marcel} → ¬i se taie capul lui(marcel) (4)
(3) + (4) →  (clauza vidă)
8. Implementat, i predicatul gen(+Sample, +Length, -Output), care produce ı̂n Output o listă
de lungime Length care constă din repetări ale listei Sample.
Exemplu: g([1, 2, 3], 10, X) leagă X la [1, 2, 3, 1, 2, 3, 1, 2, 3, 1].
Explicat, i cum funct, ionează implementarea.
Solut, ie:

g(_, 0, []).
g([E|T], Len, [E|R]) :- Len > 0, Len1 is Len - 1, append(T, [E], T1), g(T1, Len1, R).

9. Folosit, i o singură dată unul dintre predicatele findall, forall, bagof, setof pentru a
implementa predicatul minmax(+L, -Min, -Max) care leagă Min, respectiv Max, la elemen-
tul minim, respectiv maxim, al listei. Nu utilizat, i recursivitate explicită. Explicat, i cum
funct, ionează solut, ia.
Solut, ie:

minmax(L, Min, Max) :- member(Min, L), member(Max, L),


forall(member(X, L), (X >= Min, X =< Max)).

10. În acest exercit, iu urmărim crearea s, i exploatarea unei tabele de frecvent, ă pentru caracterele
care apar ı̂n cuvinte diferite dintr-un text. O tabelă de frecvent, ă este o colect, ie de asocieri
ı̂ntre un caracter s, i frecvent, a sa de aparit, ie.
Utilizat, i următorul schelet:

import Data.List
import Data.Char
import Data.Maybe

• (10p) Să se creeze aliasul de tip FreqTable, pentru tabele de frecvent, ă.
Să se implementeze funct, ia rareChars, care primes, te o tabelă de frecvent, ă s, i un număr
n s, i ı̂ntoarce lista caracterelor din tabelă care au frecvent, a mai mică decât n.
Exemplu:
*Main> rareChars [('a',3), ('b',1), ('c',2), ('d',1)] 2
"bd"
type FreqTable = Int -- aici trebuie modificat ^
ın altceva dec^
at Int

rareChars :: FreqTable -> Int -> [Char]


rareChars = undefined
• (10p) Să se implementeze funct, ia insChar, care inserează un caracter ı̂ntr-o tabelă de
frecvent, ă (dacă el exista deja ı̂n tabelă, atunci frecvent, a este actualizată).
Exemplu:
*Main> insChar 'e' [('a',3), ('b',1), ('c',2), ('d',1)]
[('e',1),('a',3),('b',1),('c',2),('d',1)]
*Main> insChar 'c' [('a',3), ('b',1), ('c',2), ('d',1)]
[('c',3),('a',3),('b',1),('d',1)] -- s-a actualizat frecvent, a lui c
insChar :: Char -> FreqTable -> FreqTable
insChar = undefined
• (10p) Să se implementeze funct, iile textToStr s, i textToTable.
textToStr primes, te un text s, i creează un string ı̂n care fiecare cuvânt din text apare o
singură dată, s, i sunt eliminate spat, iile albe.
textToTable primes, te un text s, i creează o tabelă de frecvent, ă pentru cuvintele distincte
din acesta (folosind rezultatul lui texToStr).
Util:
words :: String -> [String] -- ^
ımparte un text ^
ın cuvinte, pars^
and după
,→ spat, ii
Exemplu:
*Main> textToStr "copacii albi copacii negri"
"copaciialbinegri"
*Main> textToTable "copacii albi copacii negri"
[('c',2),('o',1),('p',1),('a',2),('i',4),('l',1),('b',1),('n',1),('e',1),('g',1),('r',1)]
textToStr :: String -> String
textToStr = undefined

textToTable :: String -> FreqTable


textToTable = undefined

Solut, ie:

--1.
type FreqTable = [(Char, Int)]

rareChars :: FreqTable -> Int -> [Char]


rareChars table n = [ c | (c, f) <- table, f < n ]

--2.
insChar :: Char -> FreqTable -> FreqTable
insChar c table = case lookup c table of -- nu e nevoie de lookup, merge s, i
,→ filter simplu sau recursivitate
Nothing -> (c, 1) : table
Just f -> (c, f+1) : filter ((/= c) . fst) table

--3.
textToStr :: String -> String
textToStr = concat . nub . words -- duplicatele se pot elimina s, i cu un fold
,→ simplu (ca la lab3)

textToTable :: String -> FreqTable


textToTable = foldr insChar [] . textToStr
Varianta C – Examen PP (2020)

1. (λx y z.corp a b c) este forma prescurtată de a scrie (((λx.λy.λz.corp a) b) c).


Fie următoarele definit, ii ı̂n Calcul Lambda:
null = λx.true
cons = λx y z.(z x y)
s, i fie operatorul pe liste:
op = λL.(L λx y.false)
Descriet, i pas cu pas (nu efectuat, i mai multe β-reduceri deodată) comportamentul lui op pe
valori de tip listă, astfel ı̂ncât să putet, i conchide la final că op se comportă ca unul dintre
operatorii pe liste cunoscut, i (s, i precizat, i care este operatorul a cărui identitate a fost ascunsă
prin folosirea numelui ”op”).
Solut, ie:
(op null) = (λL.(L λx y.false) λx.true) → (λx.true λx y.false) → true
(op (cons a b)) = (op (λx y z.(z x y) a b)) → (op λz.(z a b))
(op λz.(z a b)) = (λL.(L λx y.false) λz.(z a b)) → (λz.(z a b) λx y.false) →
(λx y.false a b) → false
Concluzie: op este null?

2. Ce tip de recursivitate are implementarea de mai jos?

(define (t x)
(cond ((or (< x 0) (even? x)) 0)
((< x 5) (quotient x 2))
(else (+ (t (- x 2)) (t (- x 4))))))

Definit, i (tot ı̂n Racket) cât mai eficient funct, ia tt care are acelas, i efect cu t, dar foloses, te un
alt tip de recursivitate.
Care este noul tip de recursivitate?
Solut, ie:
Recursivitate arborescentă – o vom transforma ı̂n recursivitate pe coadă.

(define (tt x) ;; e fix fibonacci, doar că din 2 ^


ın 2
(let t ((x x) (a 0) (b 1))
(cond ((or (< x 0) (even? x)) 0)
((= x 1) a)
((= x 3) b)
(else (t (- x 2) b (+ a b))))))

3. Atent, ie: Acest exercit, iu nu se punctează decât dacă este rezolvat fără a folosi recursivitate
explicită (deci nici letrec sau named let). Solut, iile explicit recursive se punctează cu 0.
Se dă o listă L care cont, ine numere s, i/sau liste de numere (maxim un nivel de imbricare).
Să se definească, ı̂n Racket, funct, ia longest-list care ı̂ntoarce lista de lungime maximă din
input - fie ı̂ntreg L, fie una din listele interioare lui L.
ex: (longest-list ’(0 1 2 (3 4 3 4 3 4 3 4 3 4) 5 (6) 7)) va ı̂ntoarce ’(3 4 3 4
3 4 3 4 3 4) - pt ca are lungimea 10
(longest-list ’(0 1 2 (3 4 3 4 3 4) 5 (6) 7)) va ı̂ntoarce ’(0 1 2 (3 4 3 4 3 4)
5 (6) 7)) - pt ca are lungimea 7
Solut, ie:

(define (longest-list L)
(let ((inner-lists (filter list? L)))
(foldr (lambda (L acc) (if (> (length L) (length acc)) L acc))
L
inner-lists)))
4. Definit, i ı̂n Racket fluxul coeficient, ilor binomiali:

'((1) (1 1) (1 2 1) (1 3 3 1) (1 4 6 4 1) (1 5 10 10 5 1) ...)

Solut, ie:

(define binomial-stream
(stream-cons '(1)
(stream-map (lambda (L) (let ([M (cons 0 L)]) (map + M (reverse M))))
binomial-stream)))

5. Sintetizat, i ı̂n Haskell, pas cu pas, tipul funct, iei f:

f x y = f (f x y) (f y x)

Solut, ie:

f :: a -> b -> c
x :: a
y :: b
a = b
c = a = b
f :: a -> a -> a

6. Scriet, i o instant, ă posibilă a clasei de mai jos, cont, inând o implementare neconstantă a
funct, iei f.

class MyClass c where


f :: Num a => c a -> c a -> c a

Solut, ie:

instance MyClass Maybe where


f (Just x) (Just y) = Just $ x + y
f _ _ = Nothing

7. Folosit, i pentru scriere NOT(), AND s, i OR ca operatori logici s, i ORICARE s, i EXISTĂ drept cuantifi-
catori. Scriet, i cu caps ca să nu existe confuzii cu limbajul obis, nuit. Putet, i prescurta numele
predicatelor.
S, tiind că ”Unde intră soarele pe fereastră, nu intră doctorul pe us, ă.”, că doctor(hipocrate),
că locuint, ă(apartamentul 2), s, i că intră pe us, ă(hipocrate, apartamentul 2), dorim
să demonstrăm că NOT(intră soarele pe vreo fereastră din(apartamentul 2)).

a) Traducet, i proverbul ı̂n LPOI (FOL) utilizând numai predicatele ment, ionate ı̂n problemă.
b) Enumerat, i toate clauzele necesare demonstrat, iei (inclusiv negarea concluziei), ilustrând
pe scurt cum at, i ajuns la ele.

Obs: nu se cere demonstrat, ia propriu-zisă.


Solut, ie:

a) ∀X.∀Y.locuint, ă(X) ∧ intră soarele pe vreo fereastră din(X) ∧ doctor(Y) ⇒


¬intră pe us, ă(Y, X)
b) Clauze:
(1) doctor(hipocrate)
(2) locuint, ă(apartamentul 2)
(3) intră pe us, ă(hipocrate, apartamentul 2)
(4) intră soarele pe vreo fereastră din(apartamentul 2)
(5) ¬locuint, ă(X) ∨ ¬intră soarele pe vreo fereastră din(X) ∨ ¬doctor(Y) ∨
¬intră pe us, ă(Y, X)
8. Implementat, i predicatul a(+L1, +L2, +L3, -L) care primes, te 3 liste s, i produce ı̂n L o listă
de tripluri cu elementele ce corespund din cele 3 liste. Lista L are lungimea celei mai scurte
liste dintre L1, L2 s, i L3. Exemplu: a([1, 2], [a, b, c], [x, y, z, t], X) leagă X la
[(1, a, x), (2, b, y)].
Explicat, i cum funct, ionează implementarea.
Solut, ie:

a([], _, _, []).
a(_, [], _, []).
a(_, _, [], []).
a([H1|L1], [H2|L2], [H3|L3], [(H1, H2, H3) | L123]) :- a(L1, L2, L3, L123).

9. Folosit, i unul sau mai multe dintre predicatele findall, forall, bagof, setof pentru a im-
plementa predicatul secondMin(+L, -X) care găses, te al doilea cel mai mic element din lista L
(care cont, ine cel put, in 2 elemente s, i nu cont, ine duplicate). Nu utilizat, i recursivitate explicită.
Explicat, i cum funct, ionează solut, ia.
Solut, ie:

secondMin(L, X) :- member(X, L), findall(Y, (member(Y, L), Y < X), [_]).

10. Problema urmăres, te reprezentarea unor propozit, ii din logica propozit, ională s, i implementarea
unei transformări asupra acestora.
Utilizat, i următorul schelet:

• (8p) Elaborat, i reprezentarea propozit, iilor logice ı̂n forma unui tip de date Haskell.
Propozit, iile vor putea consta ı̂n:
– propozit, ii simple (exemplu: p, q, r etc.)
– negat, ii (exemplu: ¬p)
– conjunct, ii (exemplu: p ∧ q)
– disjunct, ii (exemplu: p ∨ q).
data Prop
= Undefined
• (2p) Pornind de la reprezentarea de mai sus, traducet, i propozit, ia ¬(p ∧ (¬q ∨ r)).
prop :: Prop
prop = undefined
• (10p) Instant, iat, i clasa Show cu tipul Prop. Reprezentarea sub formă de s, ir a propozit, iei
prop ar trebui să fie cea din cerint, a de mai sus.
instance Show Prop where
show = undefined
• (10p) Implementat, i funct, ia processNeg, care urmăres, te introducerea negat, iilor ı̂n paran-
teze. De exemplu:
show (processNeg prop) va furniza (¬p ∨ (q ∧ ¬r))
processNeg :: Prop -> Prop
processNeg = undefined

Solut, ie:

-- 1.1
data Prop
= Simple Char
| Neg Prop
| And Prop Prop
| Or Prop Prop
-- 1.2
prop :: Prop
prop = Neg $ Simple 'p' `And` (Neg (Simple 'q') `Or` Simple 'r')
-- 2
instance Show Prop where
show (Simple c) = [c]
show (Neg p) = "~" ++ show p
show (And p1 p2) = "(" ++ show p1 ++ " ^ " ++ show p2 ++ ")"
show (Or p1 p2) = "(" ++ show p1 ++ " v " ++ show p2 ++ ")"
-- 3
processNeg :: Prop -> Prop
processNeg p@(Simple _) = p
processNeg (Neg p) = case p of
Simple _ -> Neg p
Neg p' -> processNeg p'
And p1 p2 -> processNeg $ Or (Neg p1) (Neg p2)
Or p1 p2 -> processNeg $ And (Neg p1) (Neg p2)
processNeg (And p1 p2) = And (processNeg p1) (processNeg p2)
processNeg (Or p1 p2) = Or (processNeg p1) (processNeg p2)
Varianta D – Examen PP (2020)

1. (λx y z.corp a b c) este forma prescurtată de a scrie (((λx.λy.λz.corp a) b) c).


Fie următoarea definit, ie ı̂n Calcul Lambda:
cons = λx y z.(z x y)
s, i fie operatorul pe liste nevide:
op = λL.(L λx y.y)
Descriet, i pas cu pas (nu efectuat, i mai multe β-reduceri deodată) comportamentul lui op pe
valori de tip listă nevidă, astfel ı̂ncât să putet, i conchide la final că op se comportă ca unul
dintre operatorii pe liste cunoscut, i (s, i precizat, i care este operatorul a cărui identitate a fost
ascunsă prin folosirea numelui ”op”).
Solut, ie:
(op (cons a b)) = (op (λx y z.(z x y) a b)) → (op λz.(z a b))
(op λz.(z a b)) = (λL.(L λx y.y) λz.(z a b)) → (λz.(z a b) λx y.y) →
(λx y.y a b) → b
Concluzie: op este cdr

2. Ce tip de recursivitate are implementarea de mai jos?

(define (w x)
(let h ((x x) (r 0))
(if (null? x)
(* 2 r)
(add1 (h (cdr x) (if (even? (car x)) r (add1 r)))))))

Definit, i (tot ı̂n Racket) cât mai eficient funct, ia ww care are acelas, i efect cu w, dar foloses, te un
alt tip de recursivitate.
Precizat, i pe scurt care modificare din cod a dus la modificarea tipului de recursivitate.
Solut, ie:
Recursivitate pe stivă – o vom transforma ı̂n recursivitate pe coadă.

(define (ww x)
(let h ((x x) (r 0))
(if (null? x)
r
(h (cdr x) (if (even? (car x)) (add1 r) (+ r 3)))))) ;; ^ınainte
,→ aveam add1 pe apelul recursiv (stivă), acum apelul recursiv
,→ este la coadă, toate adunările se fac asupra acumulatorului r
3. Atent, ie: Acest exercit, iu nu se punctează decât dacă este rezolvat fără a folosi recursivitate
explicită (deci nici letrec sau named let). Solut, iile explicit recursive se punctează cu 0.
Se dă o listă L de numere. Să se definească, ı̂n Racket, funct, ia count-smaller care ı̂ntoarce o
listă de perechi de forma (număr n din L . c^ ate valori din L sunt mai mici ca n). Atent, ie,
fiecare număr din L trebuie să apară o singură dată ı̂n partea din stânga a perechilor!
ex: (count-smaller ’(1 2 3 2 4 3 1 2)) va ı̂ntoarce ’((4 . 7) (3 . 5) (2 . 2)
(1 . 0)) - sau aceleas, i 4 perechi ı̂n orice altă ordine (ordinea NU contează), pentru că ı̂n
L sunt 7 numere mai mici ca 4, 5 mai mici ca 3, 2 mai mici ca 2, s, i niciunul mai mic ca 1.
Solut, ie:

(define (count-smaller L)
(let ((no-dups (foldl (lambda (n acc) (if (member n acc) acc (cons n
,→ acc))) '() L)))
(map (lambda(n) (cons n (length (filter (lambda (x) (< x n)) L))))
,→ no-dups)))
4. Se dau ı̂n Racket două fluxuri de liste, s1 s, i s2. Definit, i fluxul ale căror elemente se obt, in
prin intersectarea listelor de pe aceeas, i pozit, ie din s1 s, i s2.
Exemplu: s1 = ’((1 2) (3 4 5) ...), s2 = ’((2 3) (1 4 5) ...), rezultat = ’((2)
(4 5) ...)
Solut, ie:

(define result
(stream-zip-with (lambda (L1 L2) (filter (lambda (e) (member e L1)) L2))
,→ s1 s2))
5. Sintetizat, i ı̂n Haskell, pas cu pas, tipul funct, iei f:

f x y = f (y, y) [x]

Solut, ie:

f :: a -> b -> c
x :: a
y :: b
a = (b, b)
b = [a]
a = ([a], [a])
eroare

6. Supraı̂ncărcat, i ı̂n Haskell operatorul de comparat, ie pe liste, astfel ı̂ncât o listă să fie mai
mică sau egală cu alta, dacă toate elementele din prima listă sunt mai mici sau egale cu
toate elementele din a doua. Spre exemplu, [2, 3, 1] <= [5, 4, 3], dar nu avem că
[2, 4] <= [3, 5].
Solut, ie:

instance Ord a => Ord [a] where


xs <= ys = maximum xs <= minimum ys

7. Folosit, i pentru scriere NOT(), AND s, i OR ca operatori logici s, i ORICARE s, i EXISTĂ drept cuantifi-
catori. Scriet, i cu caps ca să nu existe confuzii cu limbajul obis, nuit. Putet, i prescurta numele
predicatelor.
Considerăm proverbul ”Cine fură azi un ou, mâine va fura un bou.”, s, i predicatele:

fură_azi(Cine, Ce).
fură_m^
aine(Cine, Ce).
bou(Ce).
ou(Ce).

a) Traducet, i proverbul ı̂n LPOI (FOL), utilizând numai predicatele ment, ionate.
b) Traducet, i propozit, ia ı̂n LPOI ı̂n formă clauzală (FNC).

Solut, ie:

a) ∀x.∀y.ou(x) ∧ fură azi(y, x) ⇒ ∃z.bou(z) ∧ fură m^


aine(y, z)
b) Clauze:
(1) ¬ou(x) ∨ ¬fură azi(y, x) ∨ bou(bou furat(y))
(2) ou(x) ∨ ¬fură azi(y, x) ∨ fură m^
aine(y, bou furat(y))

8. Implementat, i predicatul mul(+Lin, -Lout) care primes, te o listă de numere ca prim argument
s, i leagă al doilea argument la lista acelor numere din prima listă care sunt divizibile cu toate
celelalte numere care le urmează.
Exemplu: mul([24, 4, 12, 6, 3, 2], X) leagă X la [24, 12, 6, 2].
Explicat, i cum funct, ionează implementarea.
Solut, ie:

check(_, []).
check(X, [H|T]) :- mod(X, H) =:= 0, check(X, T).
mul([], []).
mul([H|LIn], [H|LOut]) :- check(H, LIn), !, mul(LIn, LOut).
mul([_|LIn], LOut) :- mul(LIn, LOut).

9. Folosit, i unul sau mai multe dintre predicatele findall, forall, bagof, setof pentru a imple-
menta predicatul secondMax(+L, -X) care găses, te al doilea cel mai mare element din lista L
(care cont, ine cel put, in 2 elemente s, i nu cont, ine duplicate). Nu utilizat, i recursivitate explicită.
Explicat, i cum funct, ionează solut, ia.
Solut, ie:

secondMin(L, X) :- member(X, L), findall(Y, (member(Y, L), Y < X), [_]).

10. Problema urmăres, te reprezentarea arborilor S, I-SAU s, i operat, ii de bază asupra acestora.
Un arbore S, I-SAU este un arbore următoarele caracteristici:

• nodurile care nu sunt frunze sunt de tipul S, I (AND) sau de tipul SAU (OR) s, i pot
avea oricât, i copii.
• orice nod este o etichetă care poate fi SUCCESS, FAILURE, sau UNKNOWN.

• (10p) Elaborat, i reprezentarea arborilor S, I-SAU. La nevoie, putet, i utiliza s, i alte tipuri
auxiliare.
data AndOrTree
= Undefined
• (10p) Instant, iat, i clasa Show cu tipul AndOrTree. Reprezentarea sub formă de s, ir a unui
arbore va fi de forma următoare, ı̂n care frunzele sunt reprezentate prin eticheta lor, iar
nodurile S, I s, i SAU sunt reprezentate prin tipul s, i eticheta lor:
(OR|UNKNOWN (AND|UNKNOWN FAILURE (OR|UNKNOWN UNKNOWN SUCCESS) SUCCESS)
(AND|UNKNOWN SUCCESS (OR|UNKNOWN FAILURE SUCCESS)))
instance Show AndOrTree where
show = undefined
• (10p) Implementat, i funct, ia compute care primes, te un arbore S, I-SAU s, i produce un
arbore S, I-SAU ı̂n care au fost calculate etichetele pentru noduri, după regulile:
– un nod S, I are eticheta SUCCESS dacă tot, i copiii au eticheta SUCCESS, altfel
FAILURE.
– un nod SAU are eticheta SUCCESS dacă cel put, in un copil are eticheta SUC-
CESS, altfel FAILURE.
– toate frunzele rămân nemodificate.
De exemplu, pentru arborele reprezentat ca ı̂n exemplul de la punctul anterior, rezultatul
funct, iei compute este afis, at ca:
(OR|SUCCESS (AND|FAILURE FAILURE (OR|SUCCESS UNKNOWN SUCCESS) SUCCESS)
(AND|SUCCESS SUCCESS (OR|SUCCESS FAILURE SUCCESS)))
compute :: AndOrTree -> AndOrTree
compute = undefined

Solut, ie:

-- 1
data AndOrTree
= AND Tag [AndOrTree]
| OR Tag [AndOrTree]
| Leaf Tag
data Tag = SUCCESS | FAILURE | UNKNOWN deriving (Show, Eq)
-- 2
instance Show AndOrTree where
show (AND tag children) = intern "AND" tag children
show (OR tag children) = intern "OR" tag children
show (Leaf tag) = show tag

intern t tag chd = "(" ++ t ++ "|" ++ show tag ++ concatMap ((" " ++) .
,→ show) chd ++ ")"

-- 3
getTag (AND t _) = t
getTag (OR t _) = t
getTag (Leaf t) = t

compute :: AndOrTree -> AndOrTree


compute l@(Leaf tag) = l
compute (AND tag children) = AND (if all ((==SUCCESS) . getTag) chd then
,→ SUCCESS else FAILURE) chd
where chd = map compute children
compute (OR tag children) = OR (if any ((==SUCCESS) . getTag) chd then
,→ SUCCESS else FAILURE) chd
where chd = map compute children
Examen PP – Seria 2CC — NOT EXAM MODE
29.05.2018
atent, ie: Avet, i 2 ore · 100p pentru nota maximă · Justificat, i răspunsurile!

1. Reducet, i la forma normală următoarea expresie, ilustrând pas, ii de reducere:


λx.λy.((λx.λy.x (y x)) (x y))
Solut, ie:
λx.λy.((λx.λy.x (y x)) (x y)) → − α λx.λy.((λx.λz.x (y x)) (x y)) → −β
λx.λy.(λz.(y x) (x y)) → − β λx.λy.(y x)
2. Care este diferent, a ı̂ntre următoarele două linii de cod Racket
(let ((a 1) (b 2)) (let ((b 3) (c (+ b 2))) (+ a b c)))
(let* ((a 1) (b 2)) (let* ((b 3) (c (+ b 2))) (+ a b c)))
Solut, ie:
În prima definit, ia (b 3) nu este vizibilă ı̂n legarea lui c; rezultatul este 8, iar ı̂n a
doua linie rezultatul este 9.

3. Scriet, i ı̂n Racket o funct, ie echivalentă cu zip din Haskell, s, tiind că
zip :: [a] -> [b] -> [(a, b)]. Folosit, i cel put, in o funct, ională.
Solut, ie:
(define (zip L1 L2) (map cons L1 L2))

4. Sintetizat, i tipul funct, iei f ı̂n Haskell: f x y z = x y . z


Solut, ie:
y :: t
z :: a →− b
x y :: b →− c
x :: t →− b →
− c
f x y z :: a →− c
f :: (t →− b →
− c) →
− t →
− (a →
− b) →
− a →
−c

5. Instant, iat, i clasa Show pentru funct, ii Haskell care iau un argument numeric, astfel
ı̂ncât afis, area unei funct, ii f va produce afis, area rezultatelor aplicării funct, iei pe
numerele de la 1 la 10. E.g. afis, area lui (+1) va produce: 234567891011.
Solut, ie:
-# LANGUAGE FlexibleInstances #- -- nu este cerut ^
ın rezolvarea din examen
instance (Enum a, Num a, Show b) => Show (a -> b) where
show f = concatMap (show . f) [1..10]
-- Enum nu este cerut ^
ın rezolvarea din examen

6. Folosit, i list comprehensions pentru aproduce fluxul listelor formate din primii 5
multipli ai fiecărui număr natural:
[[1,2,3,4,5],[2,4,6,8,10],[3,6,9,12,15],[4,8,12,16,20] ...] .
Solut, ie:
[take 5 [m | m <- [n..], mod m n == 0] | n <- [1..]]

7. Folosit, i rezolut, ia pentru a demonstra că dacă Ion este om s, i orice om are o bicicletă
atunci este adevărat că Ion are bicicletă sau Ion este bogat (folosit, i predicatele
om(X), areBicicletă(X) s, i bogat(X)).
Solut, ie:
Avem premisele: om(ion) s, i ∀x.om(x) ⇒ areBicicletă(x)
Concluzia: areBicicletă(ion) ∨ bogat(ion)
Clauzele:
(a) {om(ion)}
(b) {¬om(x) ∨ areBicicletă(x)}
(c) {¬areBicicletă(ion)} (prima parte a concluziei negate)
(d) {¬bogat(ion)} (a doua parte a concluziei negate)
(b) + (c) {x ←
− ion} →− ¬om(ion)(e)
(a) + (e) →
− clauza vidă

8. Scriet, i un predicat Prolog diff(A, B, R) care leagă R la diferent, a mult, imilor (repre-
zentate ca liste) A s, i B.
Solut, ie:
intersect(A, B, R) :- findall(X, (member(X, A), member(X, B)), R).

9. Dat fiind un s, ir de date binare, scriet, i un algoritm Markov care plasează la sfârs, itul
s, irului suma modulo 2 a bit, ilor din s, ir. Exemple: 101010110000111

− 1010101100001110; 100110110110 →
− 1001101101101; 100110110111 →
− 1001101101110
Solut, ie:
CheckSum; 0,1 g1
ag1 0 →− 0ag1
a01 →− 1a1
a11 →− 1a0
a→− .

− a0

10. Considerăm o structură de date de tip listă circulară, caracterizată de cont, inutul
său s, i de un cursor intrinsec structurii, pozit, ionat la orice moment pe un element al
listei. Avem următoarele funct, ionalităt, i:
• Structura va putea fi creată pe baza unei liste obis, nuite L; la creare cursorul
va fi init, ial pozit, ionat pe elementul care era primul element din L;
• Operat, ia get, care ı̂ntoarce elementul de la pozit, ia unde este cursorul;
• Operat, ia next, care avansează cursorul cu o pozit, ie spre dreapta;
Exemplu: avem lista circulară C, construită pe baza listei 1,2,3,1,5. Astfel:
get(C) = 1 get(next(next(next(next(C))))) = 5
get(next(C)) = 2 get(next(next(next(next(next(C)))))) = 1
Se cere implementarea ı̂n Racket, Haskell sau Prolog a celor 3 funct, ionalităt, i: cre-
area listei circulare, operat, ia get s, i operat, ia next.
Solut, ie:
Exemplu ı̂n Haskell:
circular l = concat . repeat $ l -- desfăs, urăm lista originală
get = head -- elementul curent
next = tail -- avansăm cursorul

2
Examen PP – Seria 2CC — NOT EXAM MODE
29.05.2018
atent, ie: Avet, i 2 ore · 100p pentru nota maximă · Justificat, i răspunsurile!

1. Reducet, i la forma normală următoarea expresie, ilustrând pas, ii de reducere:


λx.λy.((λx.λy.y (x y)) (y x))
Solut, ie:
λx.λy.((λx.λy.y (x y)) (y x)) → − β λx.λy.(λy.y (y x)) → − β λx.λy.(y x)
2. Care este diferent, a ı̂ntre următoarele două linii de cod Racket
(let* ((a 1) (b 2) (c (+ a 2))) (+ a b c))
((lambda (a b c) (+ a b c)) 1 2 (+ a 2))
Solut, ie:
În a doua linie a nu este vizibil la invocarea funct, iei λ; prima linie dă 6, a doua dă
eroare.

3. Scriet, i ı̂n Racket o funct, ie echivalentă cu unzip din Haskell, s, tiind că
unzip :: [(a, b)] -> ([a], [b]). Folosit, i cel put, in o funct, ională.
Solut, ie:
(define (unzip L) (cons (map car L) (map cdr L)))

4. Sintetizat, i tipul funct, iei f ı̂n Haskell: f x y z = x . y z


Solut, ie:
z :: t
y :: t →− a →
− b
y z :: a →− b
x :: b →− c
f x y z :: a →− c
f :: (b →− c) →− (t →
− a →
− b) →
− t →
− a →
− c

5. Instant, iat, i clasa Show pentru funct, ii Haskell care iau un argument numeric, astfel
ı̂ncât afis, area unei funct, ii f va produce afis, area rezultatelor aplicării funct, iei pe
numerele de la 1 la 10. E.g. afis, area lui (+1) va produce: 234567891011.
Solut, ie:
-# LANGUAGE FlexibleInstances #- -- nu este cerut ^
ın rezolvarea din examen
instance (Enum a, Num a, Show b) => Show (a -> b) where
show f = concatMap (show . f) [1..10]
-- Enum nu este cerut ^
ın rezolvarea din examen

6. Folosit, i list comprehensions pentru aproduce fluxul listelor de divizori pentru nu-
merele naturale: [[1], [1, 2], [1, 3], [1, 2, 4], [1, 5], [1, 2, 3, 6] ...] .
Solut, ie:
[[d | d <- [1..n], mod n d == 0] | n <- [1..]]

7. Folosit, i rezolut, ia pentru a demonstra că dacă George este t, ăran s, i orice t, ăran are
o sapă atunci este adevărat că George este des, tept sau George are o sapă (folosit, i
predicatele t, ăran(X), areSapă(X) s, i des, tept(X)).
Solut, ie:
Avem premisele: t, ăran(george) s, i ∀x.t, ăran(x) ⇒ areSapă(x)
Concluzia: areSapă(george) ∨ des, tept(george)
Clauzele:
(a) {t, ăran(george)}
(b) {¬t, ăran(x) ∨ areSapă(x)}
(c) {¬areSapă(george)} (prima parte a concluziei negate)
(d) {¬des, tept(george)} (a doua parte a concluziei negate)
(b) + (c) {x ← − george} → − ¬t, ăran(george)(e)
(a) + (e) → − clauza vidă

8. Scriet, i un predicat Prolog intersect(A, B, R) care leagă R la intersect, ia mult, imilor


(reprezentate ca liste) A s, i B.
Solut, ie:
diff(A, B, R) :- findall(X, (member(X, A), \+ member(X, B)), R).

9. Dat fiind un s, ir de date binare, scriet, i un algoritm Markov care plasează la sfârs, itul
s, irului suma modulo 2 a bit, ilor din s, ir. Exemple: 101010110000111

− 1010101100001110; 100110110110 →
− 1001101101101; 100110110111 →
− 1001101101110
Solut, ie:
CheckSum; 0,1 g1
ag1 0 →− 0ag1
a01 →− 1a1
a11 →− 1a0
a→− .

− a0

10. Considerăm o structură de date de tip listă circulară, caracterizată de cont, inutul
său s, i de un cursor intrinsec structurii, pozit, ionat la orice moment pe un element al
listei. Avem următoarele funct, ionalităt, i:
• Structura va putea fi creată pe baza unei liste obis, nuite L; la creare cursorul
va fi init, ial pozit, ionat pe elementul care era ultimul element din L;
• Operat, ia get, care ı̂ntoarce elementul de la pozit, ia unde este cursorul;
• Operat, ia prev, care deplasează cursorul cu o pozit, ie spre stânga;
Exemplu: avem lista circulară C, construită pe baza listei 1,2,3,1,5. Astfel:
get(C) = 5 get(prev(prev(prev(prev(C))))) = 1
get(prev(C)) = 1 get(prev(prev(prev(prev(prev(C)))))) = 5
Se cere implementarea ı̂n Racket, Haskell sau Prolog a celor 3 funct, ionalităt, i: cre-
area listei circulare, operat, ia get s, i operat, ia prev.
Solut, ie:
Exemplu ı̂n Prolog:
circular(L, LC) :- reverse(L, LC).% inversăm lista. Elementul curent este ultimul
element din L
get(LC, Current) :- LC = [Current | ] . % elementul current
next(LC, LCNew) :- LC = [Current, Rest], append(Rest, [Current], LCNew). % rotim s, i
mergem la următorul element, ca s, i cum ^
ın lista originală am fi mers la precedentul.

2
Examen PP varianta A — NOT EXAM MODE
07.06.2022
atent, ie: Avet, i 2 ore · 1-9: 10p; 10: 30p · 100p pentru nota maximă · Justificat, i răspunsurile!

1. Reducet, i expresia lambda (λy.λx.(y y) (x x))


Solut, ie:
α λy.

→ (λy.λz.(y y) (x x)) −→ λz.((x x) (x x))
2. Care dintre cele două adunări se vor realiza ı̂n codul Racket de mai jos? Justificat, i!
(define y 10)
((lambda (x) (lambda (y) (if (> x 2) (+ 1 y) (+ x y)) )) 5)
Solut, ie:
Niciuna, pentru că λy. nu este aplicat. Expresia ı̂ntoarce rezultatul aplicării lui λx., care este o
procedură.
3. Date fiind două liste de numere L1 s, i L2, scriet, i ı̂n Racket codul care produce o listă de perechi de
forma (x . L), unde x este un element din L1, iar L este lista elementelor din L2 care sunt divizori
ai lui x. E.g. pentru L1 = (25 30 100) s, i L2 = (2 3 5) rezultatul este ((25 . (2 5)) (30 . (2
3 5)) (100 . (2 5))). Nu folosit, i recursivitate explicită. Explicat, i cum funct, ionează codul.
Solut, ie:
(define (divpairs L1 L2) (map
(λ (x) (cons x (filter (λ (n) (zero? (remainder x n))) L2))) L1))
(divpairs ’(25 30 100) ’(2 3 5))
4. a. Ce efect are următorul cod Racket:
(define a (lambda (f g L) (if (null? L) ’()
(append (if (f (car L)) (list (g (car L))) ’()) (a f g (cdr L))))))
b. Rescriet, i funct, ia cu funct, ionale, evitând recursivitatea explicită.
Solut, ie:
a. Aplică funct, ia g fiecărui element din L pentru care funct, ia f este adevărată s, i ı̂ntoarce o listă cu
rezultatele acestor aplicări.
b. (define (afunc f g L) (map g (filter f L)))
5. Sintetizat, i tipul funct, iei de mai jos ı̂n Haskell. Explicat, i sinteza de tip pas cu pas!
f x y = (head x, head y) : f (tail x) (tail y)
Solut, ie:
f
f :: a →− b →− c
x :: a
y :: b
head, tail
x :: [d] = a
y :: [e] = b
cons
c = [(d, e)]

f :: [d] → − [e] →
− [(d, e)]

6. Instant, iat, i clasa Eq pentru funct, ii Haskell care iau un argument numeric, astfel ı̂ncât două funct, ii
sunt “egale” dacă valoarea lor este egală pentru fiecare număr ı̂ntreg ı̂ntre 1 s, i 10.
Solut, ie:
instance (Num a, Enum a, Eq b) => Eq (a -> b) where
f == g = and (zipWith (==) (map f [1..10]) (map g [1..10]))
Notă: Enum a nu era cerut.

7. Construit, i ı̂n Haskell funct, ia perms :: [Char] -> [[Char]] care primes, te o listă de caractere s, i ı̂ntoarce
fluxul s, irurilor formate din aceste caractere, ı̂ncepând cu s, iruri de lungime 1. De exemplu: take 45
$ perms "abc" ı̂ntoarce ["a","b","c","aa","ba","ca", . . . ,"bc","cc","aaa","baa","caa","aba","bba",
. . . , "acc","bcc","ccc","aaaa","baaa","caaa","abaa","bbaa","cbaa"]
Solut, ie:
perms alfa = (map (:"") alfa) ++ [c:s | s <- perms alfa, c <- alfa]
Construiesc s, irurile de lungime 1, apoi celelalte s, iruri sunt s, iruri deja ı̂n flux la care adaug unul dintre
caracterele din alfaetul dat.
8. S, tiind că Cum as, terne, as, a doarme s, i că ¬doarme(Ion, bine), demonstrat, i prin metoda rezolut, iei
că Ion nu a as, ternut bine.
Solut, ie:
Avem premisele: ¬doarme(Ion, bine) s, i ∀x.∀y.asterne(x, y) ⇒ doarme(x, y)
Concluzia: ¬asterne(Ion, bine)
Clauzele:
(a) {¬doarme(Ion, bine)}
(b) {¬asterne(x, y) ∨ doarme(x, y)}
(c) {asterne(Ion, bine)} (concluzia negată)
(b) + (c) {x ← − Ion, y ← − bine} → − doarme(Ion, bine) (d)
(a) + (d) → − clauza vidă
9. Construit, i ı̂n Prolog un predicat up(+L, -LUp) care produce ı̂n LUp o listă cu următoarele proprietăt, i:
ˆ primul element din LUp este acelas, i cu primul element din lista L
ˆ următorul element din LUp este următorul element din L, mai mare decât primul.
ˆ următorul element din LUp este următorul element din L, mai mare decât anteriorul din LUp, etc.
Exemplu: up([5, 6, 3, 4, 8, 5, 9, 2], LUp) leagă LUp la [5, 6, 8, 9].
Solut, ie:
% up(+L, -LUp)
up([H|T], [H|TUp]) :- upAux(H, T, TUp).
upAux( , [], []).
% H face parte din secvent, a crescătoare:
upAux(CurrentMax, [H|T], [H|TUp]) :- H > CurrentMax, upAux(H, T, TUp).
% H nu face parte din secvent, a crescătoare:
upAux(CurrentMax, [H|T], TUp) :- H =< CurrentMax, upAux(CurrentMax, T, TUp).
Alternativ, pot evita comparat, ia din ultima regulă, dacă folosesc ! după comparat, ia din penultima
regulă.

Un Trie (un arbore de prefixe) este un arbore care stochează cuvinte, după
prefixele lor comune. De exemplu, arborele din dreapta stochează cuvintele
10. ”melc”, ”mină”, ”mică”, ”mici”, ”mac”, ”mama”, s, i “ac”. Fiecare nod, ı̂n
afară de rădăcină. cont, ine o literă. Rezolvat, i următoarele cerint, e ı̂ntr-un
limbaj la alegere dintre Racket, Haskell s, i Prolog:

a1. descriet, i structura de date pentru reprezentarea unui arbore de prefixe (ı̂n Haskell, definit, i tipul
cu data, ı̂n Racket s, i Prolog descriet, i cum structurat, i arborele folosind construct, iile oferite de limbaj.
a2. scriet, i funct, ia / predicatul emptyTrie, care construies, te un arbore de prefixe vid (care nu cont, ine
niciun cuvânt).
b1. scriet, i funct, ia / predicatul canBeFollowedBy care pentru un caracter s, i un nod ı̂ntoarce adevărat
dacă nodul are un copil cu valoarea egală cu caracterul dat. În exemplu, pentru primul nod m,
funct, ia ı̂ntoarce adevărat pentru caracterele e, s, i s, i a.
b2. scriet, i funct, ia / predicatul getSubtreeFor care pentru un caracter s, i un nod ı̂ntoarce nodul copil
al nodului dat, care are valoarea egală caracterul dat. E.g. pentru caracterul e s, i primul nod m din
exemplu, funct, ia ı̂ntoarce subarborele cu rădăcina ı̂n nodul e.
c. scriet, i funct, ia / predicatul addWord, care primes, te un cuvânt s, i un arbore de prefixe, s, i adaugă
cuvântul la arbore.
Solut, ie:
Vezi fis, ier separat
Examen PP varianta B — NOT EXAM MODE
07.06.2022
atent, ie: Avet, i 2 ore · 1-9: 10p; 10: 30p · 100p pentru nota maximă · Justificat, i răspunsurile!

1. Reducet, i expresia lambda (λx.(λx.(x x) λx.x) (x x))


Solut, ie:
primul λx.
−−−−−−→ (λx.(x x) λx.x) → − (λx.x λx.x) →
− λx.x
2. Care dintre cele două adunări se vor realiza ı̂n codul Racket de mai jos? Justificat, i!
(define y 10)
((lambda (x) (if (> x 2) (lambda (y) (+ y 1)) (+ x y))) 5)
Solut, ie:
Niciuna, pentru că suntem pe ramura de true a if-ului, s, i apoi λy. nu se aplică. Rezultatul expresiei
este o procedură.
3. Date fiind două liste de numere L1 s, i L2, scriet, i ı̂n Racket codul care produce o listă de perechi de
forma (x . L), unde x este un element din L1, iar L este lista elementelor din L2 care sunt multipli ai
lui x. E.g. pentru L1 = (2 3 4) s, i L2 = (4 5 8 9 10 100) rezultatul este ((2 . (4 8 10 100)) (3
. (9)) (4 . (4 8 100))). Nu folosit, i recursivitate explicită. Explicat, i cum funct, ionează codul.
Solut, ie:
(define (mulpairs L1 L2) (map
(λ (x) (cons x (filter (λ (n) (zero? (remainder n x))) L2))) L1))
(mulpairs ’(2 3 4) ’(4 5 8 9 10 100))
4. a. Ce efect are următorul cod Racket:
(define b (lambda (f g L) (if (null? L) ’()
(append (if (f (g (car L))) (list (g (car L))) ’()) (b f g (cdr L))))))
b. Rescriet, i funct, ia cu funct, ionale, evitând recursivitatea explicită.
Solut, ie:
a. Aplică funct, ia g pe fiecare element din L, apoi păstrează ı̂n rezultat doar acele rezultate pentru
care funct, ia f este adevărată.
b. (define (bfunc f g L) (filter f (map g L)))
5. Sintetizat, i tipul funct, iei de mai jos ı̂n Haskell. Explicat, i sinteza de tip pas cu pas!
f x y = if x == head y then [x] else f x (tail y)
Solut, ie:
f
f :: a →− b →− c
x :: a
y :: b
head y, tail y
y :: [d] = b
==
(==) :: Eq t => t →
− t →
− Bool
x :: d = a, Eq a
if
[a] = c

f :: Eq a →− a →− [a] →
− [a]

6. Instant, iat, i clasa Ord pentru funct, ii Haskell care iau un argument numeric, astfel ı̂ncât o funct, ie este
“mai mică” decât alta dacă valoarea ei este mai mică decât a celeilalte funct, ii ı̂n cel put, in unul
dintre numerele ı̂ntregi ı̂ntre 1 s, i 10.
Solut, ie:
instance (Num a, Enum a, Ord b) => Ord (a -> b) where
f <= g = or (zipWith (<=) (map f [1..10]) (map g [1..10]))
Notă: Enum a nu era cerut.

7. Implementat, i ı̂n Racket fluxul ı̂n care primele 3 elemente sunt 1, 2 s, i 3, iar fiecare dintre următoarele
elemente este produsul dintre cele trei elemente anterioare.
Solut, ie:
(define (stream-zip3 f s1 s2 s3) (stream-cons (f (stream-first s1) (stream-first s2) (stream-first
s3))
(stream-zip3 f (stream-rest s1) (stream-rest s2) (stream-rest s3)) ))
(define superProducts (stream-cons 1 (stream-cons 2 (stream-cons 3
(stream-zip3 * superProducts
(stream-rest superProducts) (stream-rest (stream-rest superProducts)) ) )))) (stream->list (stream-take
superProducts 7))

8. S, tiind că Cine ı̂nvat, ă, nu moare de foame s, i că moareDe(Ion, f oame), demonstrat, i prin metoda
rezolut, iei că Ion nu ı̂nvat, ă.
Solut, ie:
Avem premisele: moareDe(Ion, f oame) s, i ∀x.invata(x) ⇒ ¬moareDe(x, f oame)
Concluzia: ¬invata(Ion)
Clauzele:
(a) {moareDe(Ion, f oame)}
(b) {¬invata(x) ∨ ¬moareDe(x, f oame)}
(c) {invata(Ion)} (concluzia negată)
(b) + (c) {x ← − Ion} → − ¬moareDe(Ion, f oame) (d)
(a) + (d) → − clauza vidă
9. Construit, i ı̂n Prolog un predicat dn(+L, -LDown) care produce ı̂n LDown o listă cu următoarele pro-
prietăt, i:
ˆ primul element din LDown este acelas, i cu primul element din lista L
ˆ următorul element din LDown este următorul element din L, mai mic decât primul.
ˆ următorul element din LDown este următorul element din L, mai mic decât anteriorul din LDown,
etc.
Exemplu: dn([5, 6, 3, 4, 8, 5, 9, 2], LDown) leagă LDown la [5, 3, 2].
Solut, ie:
% dn(+L, -LDown)
dn([H|T], [H|TDown]) :- dnAux(H, T, TDown).
dnAux( , [], []).
% H face parte din secvent, a crescătoare:
dnAux(CurrentMin, [H|T], [H|TDown]) :- H < CurrentMin, dnAux(H, T, TDown).
% H nu face parte din secvent, a crescătoare:
dnAux(CurrentMin, [H|T], TDown) :- H >= CurrentMin, dnAux(CurrentMin, T, TDown).
Alternativ, pot evita comparat, ia din ultima regulă, dacă folosesc ! după comparat, ia din penultima
regulă.

Un Trie (un arbore de prefixe) este un arbore care stochează cuvinte, după
prefixele lor comune. De exemplu, arborele din dreapta stochează cuvintele
10. ”melc”, ”mină”, ”mică”, ”mici”, ”mac”, ”mama”, s, i “ac”. Fiecare nod, ı̂n
afară de rădăcină. cont, ine o literă. Rezolvat, i următoarele cerint, e ı̂ntr-un
limbaj la alegere dintre Racket, Haskell s, i Prolog:

a1. descriet, i structura de date pentru reprezentarea unui arbore de prefixe (ı̂n Haskell, definit, i tipul
cu data, ı̂n Racket s, i Prolog descriet, i cum structurat, i arborele folosind construct, iile oferite de limbaj.
a2. scriet, i funct, ia / predicatul emptyTrie, care construies, te un arbore de prefixe vid (care nu cont, ine
niciun cuvânt).
b1. scriet, i funct, ia / predicatul countWords, care numără cuvintele stocate ı̂ntr-un arbore de prefixe.
În exemplu sunt 7 cuvinte.
b2. scriet, i funct, ia / predicatul printWords, care ı̂ntoarce o listă cu cuvintele stocate ı̂ntr-un arbore de
prefixe. Considerăm un cuvânt ca o cale de la rădăcină la o frunză. Pentru Racket s, i Prolog, folosit, i
o reprezentare a cuvintelor mai us, or de implementat, nu trebuie neapărat să fie s, iruri de caractere.
c. scriet, i funct, ia / predicatul predict, care pentru un prefix s, i un arbore de prefixe, ı̂ntoarce o listă
cu toate cuvintele care cont, in prefixul dat.
Solut, ie:
Vezi fis, ier separat
Examen PP/2CD/28.05.19 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p

1. Încercuiți aparițiile legate și subliniați aparițiile libere ale variabilelor din expresia (λx.(λy.(x y) λz.(y z)) x).

2. Pentru codul Racket de mai jos, scrieți evoluția pas cu pas a execuției lui (pow 2 5), și ilustrați stiva la fiecare pas.
(define (pow a n)
(cond ((zero? n) 1)
((odd? n) (* a (pow a (- n 1))))
(else (pow (* a a) (/ n 2)))))

(pow 2 5) []
(* 2 (pow 2 4)) [*2]
(* 2 (pow 4 2)) [*2]
(* 2 (pow 16 1)) [*2]
(* 2 (* 16 (pow 16 0))) [*16, *2]
(* 2 (* 16 1)) [*16, *2]
(* 2 16) [*2]
32 []

3. În Racket, fără a folosi recursivitate explicită:

a) Scrieți o funcție curry care primește un predicat p și o listă L și întoarce lista elementelor x din L pentru care x
respectă și x/2 (împărțire întreagă) nu respectă p.
(define (f p)
(λ (L)
(filter (λ (x) (and (p x) (not (p (quotient x 2))))) L)))

b) Folosiți funcția de mai sus, scriind minimul necesar, pentru a extrage elementele divizibile cu 2 dar nu cu 4 ale
unei liste numbers.
((f even?) numbers)

4. Folosind interfața Racket pentru fluxuri, implementați fluxul (1), (1 1), (1 1), (1 1 1), (1 1 1), (1 1 1 1), (1 1 1 1), …
(primul element o dată, apoi fiecare element de câte 2 ori). Apoi implementați același flux folosind promisiuni,
ca și cum interfața nu ar exista.
(define f1
(let iter ((a '(1)) (b '(1 1)))
(stream-cons a (stream-cons b (iter (cons 1 a) (cons 1 b))))))
(define f2
(let iter ((a '(1)) (b '(1 1)))
(cons a (delay (cons b (delay (iter (cons 1 a) (cons 1 b))))))))

5. Sintetizați tipul funcției f (în Haskell): f x y = iterate $ x . y


x :: a, y :: b, iterate $ x . y :: c, f :: a -> b -> c
iterate :: (d -> d) -> d -> [d]
=> x . y :: d -> d, c = d -> [d]
(.) :: (g -> h) -> (e -> g) -> (e -> h)
=> x :: g -> h, y :: e -> g, x . y :: e -> h
=> e = d, h = d, a = g -> d, b = d -> g
=> f :: (g -> d) -> (d -> g) -> d -> [d]

6. Folosind un list comprehension (5p), instanțiați clasa Ord pentru tipul Student de mai jos (fiecare student este
definit prin nume și o listă de triplete de forma (nume_materie, notă_materie, credite_materie)) astfel încât
studenții să fie ordonați după totalul creditelor la materii la care au obținut notă de trecere.
data Student = Student String [(String, Int, Int)] deriving (Eq, Show)
Examen PP/2CD/28.05.19 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p

instance Ord Student where


Student _ g1 <= Student _ g2 = credits g1 <= credits g2
where
credits g = sum [ cred | (_, grade, cred) <- g, grade >= 5 ]

7. Traduceți în FOL propoziția: “Orice mâță blândă ori doarme, ori îl zgârie pe vreun om rău.”

∀X●( (mâță(X) ∧ blândă(X)) ⇒ (doarme(X) ∨ ∃Y●( om(Y) ∧ rău(Y) ∧ zgârie(X, Y) )) )

8. Adăugați implementarea predicatului one_bubble (care va trece o dată prin listă interschimbând elementele
vecine care sunt în ordine strict descrescătoare) la următorul cod Prolog pentru algoritmul bubble-sort:
bubble_sort(L, L) :- one_bubble(L, L), !.
bubble_sort(L, S) :- one_bubble(L, Bubbled), bubble_sort(Bubbled, S).

one_bubble([], []).
one_bubble([X], [X]).
one_bubble([X,Y|Rest], [X|Res]) :- X =< Y, !, one_bubble([Y|Rest], Res).
one_bubble([X,Y|Rest], [Y|Res]) :- one_bubble([X|Rest], Res).

9. Pentru implementarea de mai jos, spuneți în câte moduri (și care sunt acestea, din punct de vedere al legării
variabilelor) se va satisface scopul sel(Y, [X, 2, Y, 4], 2, [1, Z, Z, 4]).
sel(X, [X|List], Y, [Y|List]).
sel(X, [X0|XList], Y, [X0|YList]) :- sel(X, XList, Y, YList).

2 moduri: Y = Z, Z = 2, X = 1 ; X = 1, Z = 2 ; false.

10. Problemă (de rezolvat în Haskell)

a) Implementați un tip de date MList pentru liste ce pot conține întregi, caractere sau perechi de întregi și caractere.

b) Implementați o funcție: filter' :: Char -> MList -> MList care filtrează o MListă astfel:
Dacă primul parametru are valoarea:
- 'i', atunci filter' întoarce o MListă ce conține doar valorile întregi
- 'c', atunci filter' întoarce o MListă ce conține doar caracterele
- 'p', atunci filter' întoarce o MListă ce conține doar perechile

c) Implementați o funcție care primește o MListă și întoarce lista caracterelor conținute, dacă în MListă se află
doar caractere, sau o valoare cu semnificație de eșec - altfel. Valoarea cu semnificație de eșec nu trebuie să se
poată obține dintr-o MListă care conține doar caractere (de exemplu, [ ] nu indică un eșec cert, ci ar putea
proveni dintr-o MListă goală).
data Val = I Int | C Char | P (Int,Char) deriving Show
data MList = M [Val]

filter' :: Char -> MList -> MList


filter' c (M l) = M $ filter (f c) l
where f 'i' (I _) = True
f 'c' (C _) = True
f 'p' (P _) = True
f _ _ = False

conv :: MList -> Maybe [Char]


conv (M ((C c):rest)) =
case (conv (M rest)) of
Just l -> Just (c:l)
Nothing -> Nothing
conv (M []) = Just []
conv _ = Nothing
Examen PP/2CD/28.05.19 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p

1. Folosind evaluare normală, aduceți la forma normală expresia (λx.(λy.(x y) λz.(y z)) x).

E → (λy.(x y) λz.(y z)) → (x λz.(y z))

2. Pentru codul Racket de mai jos, scrieți evoluția pas cu pas a execuției lui (mul 3 3), și ilustrați stiva la fiecare pas.
(define (mul x y)
(cond ((zero? y) 0)
((odd? y) (+ x (mul x (- y 1))))
(else (+ (mul x (/ y 2)) (mul x (/ y 2))))))

(mul 3 3) []
(+ 3 (mul 3 2)) [+3]
(+ 3 (+ (mul 3 1) (mul 3 1))) [+[..], +3]
(+ 3 (+ (+ 3 (mul 3 0)) (mul 3 1))) [+3, +[..], +3]
(+ 3 (+ (+ 3 0) (mul 3 1))) [+3, +[..], +3]
(+ 3 (+ 3 (mul 3 1))) [+3, +3]
(+ 3 (+ 3 (+ 3 (mul 3 0)))) [+3, +3, +3]
(+ 3 (+ 3 (+ 3 0))) [+3, +3, +3]
(+ 3 (+ 3 3)) [+3, +3]
(+ 3 6) [+3]
9 []

3. În Racket, fără a folosi recursivitate explicită:

a) Scrieți o funcție care primește o funcție binară f și două liste de aceeași lungime și întoarce lista aplicărilor
lui f asupra elementelor de pe aceeași poziție (ex: pentru +, ‘(1 2 3) și ‘(4 5 6) întoarce ‘(5 7 9)).
(define (fun f L1)
(λ (L2)
(map f L1 L2)))

b) Folosiți funcția de mai sus pentru a aduna ‘(1 2 3) la toate listele dintr-o listă de liste.
(map (fun + '(1 2 3)) L)

4. Implementați în Racket fluxul multiplilor unui număr n, folosind închideri funcționale, ca și cum interfața Racket
pentru fluxuri nu ar exista. Apoi obțineți (nu altfel decât prin extragere din flux) lista primelor 3 elemente din
acest flux (pentru n = 5).
(define (stream n)
(let iter ((a 0))
(cons a (λ () (iter (+ a n))))))
(let ((fives (stream 5)))
(list (car fives) (car ((cdr fives))) (car ((cdr ((cdr fives)))))))

5. Sintetizați tipul funcției f (în Haskell): f x = filter $ x . x . x


x :: a, filter $ x . x . x :: b, f :: a -> b
filter :: (c -> Bool) -> [c] -> [c]
=> x . x . x :: c -> Bool, b = [c] -> [c]
(.) :: (e -> g) -> (d -> e) -> (d -> g)
=> x :: e -> g, x . x :: d -> e, x . x . x :: d -> g
=> c = d, g = Bool
=> d = e = g = c = Bool
=> f :: (Bool -> Bool) -> [Bool] -> [Bool]

6. Folosind o funcție implementată cu gărzi (5p), instanțiați clasa Eq pentru tipul Student de mai jos (un student este
definit prin nume și o listă de perechi de forma (nume_materie, notă_materie)) astfel încât doi studenți sunt egali
dacă au același tip de rezultat la “PP” (există 3 tipuri: nepromovat, promovat cu nota sub 10, promovat cu nota 10).
Examen PP/2CD/28.05.19 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p
data Student = Student String [(String, Int)] deriving Show

instance Eq Student where


Student _ g1 == Student _ g2 = getRange g1 == getRange g2
where
f grade
| grade < 5 = 1
| grade < 10 = 2
| otherwise = 3
getRange g = [ f grade | ("PP", grade) <- g ]

7. Folosiți rezoluția pentru a demonstra că {a ∨ b, a ⇒ c, b ⇒ d} |= c ∨ d.

Premise: 1. {a, b}; 2. {¬a, c}; 3. {¬b, d}


Concluzie negată: 4. {¬c}; 5. {¬d}
Demonstrație: 6. {¬a} (2,4) 7. {¬b} (3,5) 8. {b} (1,6) 9. {} (7,8)

8. Folosind cut (!) pentru evitarea calculelor inutile (5p), implementați în Prolog predicatul switchUntilZero(?List1,
?List2) astfel încât List2 are aceleași elemente cu List1, cu excepția că până la întâlnirea unui eventual element 0
toate a-urile se transformă în b-uri și b-urile în a-uri.
?- switchUntilZero([a,b,1,d,b,0,a,b], R). --- R = [b, a, 1, d, a, 0, a, b].
?- switchUntilZero(R, [a,b,c,d,b,a,b]). --- R = [b, a, c, d, a, b, a].

other(a,b). other(b,a).
switchUntilZero([], []).
switchUntilZero([0|Rest], [0|Rest]) :- !.
switchUntilZero([X|Rest], [Y|Res]) :- other(X,Y), !, switchUntilZero(Rest, Res).
switchUntilZero([X|Rest], [X|Res]) :- switchUntilZero(Rest, Res).

9. Pentru implementarea de mai jos, spuneți în câte moduri (și care sunt acestea, din punct de vedere al legării
variabilelor) se va satisface scopul pred([1, 2, 3, X], S).
pred(L, S) :- append(_, B, L), append(S, _, B), S = [_,_|_].
6 moduri: S = [1, 2] ; S = [1, 2, 3] ; S = [1, 2, 3, X] ; S = [2, 3] ;
S = [2, 3, X] ; S = [3, X] ; false.

10. Problemă (de rezolvat în Haskell)

a) (10p) Definiți tipul de date polimorfic “coadă” de elemente de un tip oarecare. O coadă va fi reținută sub
forma a 2 stive (una de in, în care adăugăm, și una de out, din care scoatem (când se golește, vărsăm stiva de
in în stiva de out, astfel încât să se respecte în continuare principiul FIFO)).

b) (20p) Pentru tipul de mai sus, furnizați signaturile (4p) și implementarea (16p) următorilor operatori:
- isEmpty care întoarce True pentru coada goală, altfel False
- top care întoarce elementul de la începutul cozii
- del care scoate elementul de la începutul cozii (întoarce coada fără acesta)
- ins care introduce un nou element în coadă (evident, la sfârșit)
data Queue a = Q [a] [a] deriving Show

isEmpty :: (Queue a) -> Bool top :: (Queue a) -> a


isEmpty (Q [] []) = True top (Q xs []) = last xs
isEmpty _ = False top (Q _ (x:_)) = x

del :: (Queue a) -> (Queue a) ins :: a -> (Queue a) -> (Queue a)


del (Q xs []) = Q [] $ tail $ reverse xs ins x (Q xs ys) = Q (x:xs) ys
del (Q xs (y:ys)) = Q xs ys
Examen PP/2CD/10.06.22 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p

1. Dați exemplu de o λ-expresie care conține 2 variabile x și y, astfel încât x are 2 apariții legate și una liberă, iar y are o
apariție legată și una liberă.

Soluție (una din multe posibile): ((λx.λy.x x) y)


Barem: câte 2.5p pentru: 2 apariții legate x, o apariție liberă x, o apariție legată y, o apariție liberă y

2. In Racket, folosind recursivitate pe coadă, implementați funcția n-times-n care primește un parametru n și întoarce o
listă care conține de n ori valoarea n. (ex: (n-times-n 4) => ‘(4 4 4 4)). (7p)
Ilustrați parțial (primele 3 apeluri recursive pe coadă) evoluția pas cu pas pentru (n-times-n 10). În cazul în care
folosiți o funcție ajutătoare (sau un named let), atunci veți ilustra evoluția acesteia (acestuia). (3p)

Soluție:
(define (n-times-n n) (n-times-n 4) =>
(let loop ((i n) (acc '())) (loop 4 ‘()) =>
(if (zero? i) (loop 3 ‘(4)) =>
acc (loop 2 ‘(4 4)) …
(loop (- i 1) (cons n acc)))))
Barem:
a) 2p - funcție ajutătoare sau named let cu parametri corespunzători
1p - condiție de oprire corectă
1p - întoarce acc pe cazul de bază
3p - apel recursiv cu parametri actualizați corect
b) câte 1p pentru fiecare apel

3. În Racket, fără a folosi recursivitate explicită, scrieți o funcție curry care primește o listă de funcții și o listă de
argumente și întoarce lista rezultatelor aplicării fiecărei funcții din prima listă pe argumentul de pe poziția
corespunzătoare în a doua listă. (prima funcție pe primul argument, a doua pe al doilea, etc.) (7p)
Apelați corespunzător această funcție pe o primă listă care conține funcțiile list, odd? și null?, respectiv o a doua listă
care conține valorile ‘(1), 2 și ‘(3) (2p) și precizați ce se obține în urma apelului. (1p)

Soluție:
(define (applications funcs)
(λ (args)
(map (λ (f a) (f a)) funcs args)))

((applications (list list odd? null?)) '((1) 2 (3))) => (((1)) #f #f)

Barem:
a) 3p - forma curry pentru cei 2 parametri
2p - map sau fold cu argumente corespunzătoare (ca număr și semnificație)
2p - funcție care ia f și a din cele 2 liste și calculează (f a) (sau adaugă (f a) în acc, la fold)
b) 1p - apel corespunzător formei curry
0.5p - listele construite cu o metodă corectă (funcția list sau quote unde sunt doar valori numerice)
0.5p - conținut corect liste
c) 1p - rezultat corect

4. Folosind interfața Racket pentru fluxuri, implementați fluxul x, x^2, x^3, x^4 … etc. (5p)
Folosiți-vă de această implementare pentru a implementa fluxul de liste (2 3), (4 9), (8 27), (16 81) … etc. (5p)

Soluție (se consideră definit stream-zip-with):


(define (powers x)
(letrec ((pows (stream-cons x (stream-map (λ (e) (* x e)) pows))))
pows))
(define f
(stream-zip-with list (powers 2) (powers 3)))
Examen PP/2CD/10.06.22 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p
Barem:
a) pt generare explicită: pt generare implicită:
2p - parametri relevanți în recursivitate 1p - generare prim element
1p - generare corectă element curent 2p - funcțională apelată pe nr și tip corect de argumente
2p - apel recursiv cu parametri actualizați corect 2p - funcția (lui map, zip, etc.) face ce trebuie
b) idem a (pt generare implicită, 3p funcționala cu argumente corecte + 2p funcția)

5. Implementați funcția de la exercițiul 3 în Haskell (cu sau fără recursivitate explicită). (3p)
Sintetizați tipul acestei funcții (nu sunt necesare explicații formale, dar sunt necesare explicații). (5p)
Apelați funcția pe o primă listă care conține funcțiile odd și null și o a doua listă care conține argumentele 2 și [3] (1p)
și precizați ce se obține în urma apelului. (1p)

Soluție:
a) applications funcs args = zipWith (\f a -> f a) funcs args
b) (1) zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] (tipul funcției folosită de zipWith corespunde tipurilor elementelor listelor)
(2) \f a -> f a :: (t1 -> t) -> t1 -> t (f se aplică pe a => are un input de tipul lui a, funcția mare întoarce (f a) deci același
tip cu ce întoarce f)
din (1),(2) => a = (t1 -> t), b = t1, c = t
=> applications :: [t1 -> t] -> [t1] -> [t] (applications primește ultimele 2 argumente ale lui zipWith și întoarce ce
întoarce zipWith)
c) applications [odd, null] [2, [3]] => eroare întrucât listele nu sunt omogene (conțin elemente de tipuri diferite)

Barem:
a) 1p - luarea unui f și unui a și realizarea calculului f a
2p - zipWith sau apel recursiv cu argumente corecte (cu 0.5p pt cazul de bază, dacă se rezolvă recursiv)
b) 2p - explicație tip listă de funcții
1p - explicație tip listă de argumente
1p - explicație tip listă de rezultate
1p - explicație coincidență tip între inputul funcțiilor și argumente, între outputul funcțiilor și rezultate
c) 1p - apel scris corect
1p - semnalare eroare (explicată corect sau neexplicată, se scad 0.5p pentru eroare explicată eronat)

6. Fie definițiile următoare ale tipurilor Card (pentru cărți de joc) și Value (pentru valoarea asociată unei cărți):
data Card = C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | C10 | J | Q | K | A deriving Show
data Value = Only Int | Either Int Int deriving Show

Definiți clasa Valuable caracterizată de o singură funcție value care primește o valoare a tipului membru al clasei și
întoarce o valoare de tip Value asociată acesteia. (3p)
Adăugați tipul Card la clasa Valuable, astfel încât cărților mici (de la 2 la 9) să li se asocieze propria lor valoare,
cărților 10, J, Q, K să li se asocieze valoarea 10, iar asului (A) să i se asocieze fie valoarea 1, fie valoarea 11. (7p)

Soluție:
class Valuable t where
value :: t -> Value

instance Valuable Card where


value A = Either 1 11
value C2 = Only 2
value C3 = Only 3
value C4 = Only 4
value C5 = Only 5
value C6 = Only 6
value C7 = Only 7
value C8 = Only 8
value C9 = Only 9
value _ = Only 10
Examen PP/2CD/10.06.22 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p
Barem:
a) 0.5p - șablon class Valuable … where
0.5p - folosire variabilă de tip în definiția clasei
2p - signatură funcția value (1p input, 1p output)
b) 1p - șablon instance Valuable Card where
2p - definire value pe cărți mici
2p - definire value pe 10, J, Q, K
2p - definire value pe A

7. Demonstrați că {a, a ⇒ b, b ⇒ c} ⊨ c în două moduri: prin rezoluție (5p), respectiv cu tabele de adevăr (5p).

Soluție:
a) Cele 3 premise în formă clauzală: Rezultate ale aplicării rezoluției:
(1) {a} (4) {b} (din 1, 2)
(2) {¬a, b} (5) {c} (din 3, 4)
(3) {¬b, c}
Obs: se acceptă demonstrat așa sau prin reducere la absurd, introducând ¬c și ajungând la rezolventul vid.
b) a b c a⇒b b⇒c
T T T T T Rândul marcat este singurul care satisface premisele
T T F T F {a, a ⇒ b, b ⇒ c}, și se observă că acesta satisface și concluzia c.
T F T F T
T F F F T
….
(rândurile cu a = F oricum nu interesează pentru că nu satisfac premisele)

Barem:
a) 2p - transformarea implicațiilor în forma clauzală
3p - secvența de rezoluții care duce la {c}
b) 3p - construirea tabelei de adevăr total sau parțial cu explicația de ce restul nu contează
1p - identificarea rândurilor care satisfac premisele
1p - observația că acestea satisfac și c

8. Implementați, în Prolog, predicatul flatten care primește o listă de atomi și/sau liste (cu orice nivel de imbricare) și
întoarce o listă care conține toți atomii din lista inițială. (ex: flatten([a,[b,[c],[[d]],[],[e]]], F) => F = [a, b, c, d, e].)

Soluție:
flatten([], []).
flatten([[]|Rest], Res) :- !, flatten(Rest, Res).
flatten([[X|R]|Rest], Res) :- !, flatten([X|R], Aux1), flatten(Rest, Aux2), append(Aux1,
Aux2, Res).
flatten([X|Rest], [X|Res]) :- flatten(Rest, Res).

Barem:
1p - cazul de bază
2p - primul element listă vidă (patterns, apel recursiv)
4p - primul element listă nevidă (patterns, flatten, flatten, append)
2p - primul element atom (patterns, apel recursiv)
1p - reguli mutual exclusive

9. Se dă un graf descris prin fapte Prolog de tip nod/1 și arc/2. Folosind un metapredicat, determinați lista tuturor
perechilor de 2 noduri distincte între care există un drum de lungime 3 (3 arce). Lista nu va conține duplicate. Drumul
poate conține același nod de mai multe ori, doar nodul inițial și cel final trebuie să fie diferite.

Soluție: setof((X,Y), Z^T^(arc(X,Z), arc(Z,T), arc(T,Y), X \= Y), L).

Barem:
1p - metapredicat cu template, goal, bag
Examen PP/2CD/10.06.22 Timp: 2h Ex 1-9: câte 10p, Pb 10: 30p, pentru maxim sunt suficiente 100p/120p
1p - template pereche
3p - identificare drum de 3 arce (cu corespondența variabilelor între ele și cu cele din template)
1p - X \= Y
2p - lista nu conține duplicate (setof sau findall((X,Y), (nod(X), nod(Y), once((arc(X,Z), arc(Z,T), arc(T,Y), X \= Y))), L))
2p - rezultat sub formă de o singură listă, nu satisfaceri succesive ale unui scop (Z^T^ sau findall ca mai sus)

10. Vom folosi tipurile Card și Value definite la exercițiul 6 (puteți presupune că exercițiul 6 este deja rezolvat, și că
există funcția value care asociază fiecărei cărți valoarea acesteia) pentru a modela un joc de Blackjack. Pentru aceasta,
ne interesează doar colecțiile de cărți pentru care suma valorilor cărților este maxim 21.
a) Implementați funcția correctValue :: Value -> Maybe Value care primește o valoare de tip Value din care caută să
păstreze doar întregii care nu depășesc numărul 21. (ex: correctValue (Either 12 22) => Just (Only 12)) (10p)
b) Implementați funcția handValue :: [Card] -> Value care primește o listă de cărți și calculează suma valorilor cărților
din listă. Dacă în listă apar unul sau mai mulți ași (A), atunci rezultatul va fi de tip Either suma1 suma2, unde suma1 este
calculată pentru cazul când toți așii valorează 1, iar suma2 este calculată pentru cazul când un as valorează 11 și ceilalți
valorează 1. (Nu are sens ca mai mai mult de un as să valoreze 11, pentru că suma ar depăși 21). (ex: handValue
[A,A,J,C9] => Either 21 31) (15p)
c) Implementați point free funcția correctHandValue :: [Card] -> Maybe Value care primește o listă de cărți și
calculează suma valorilor cărților din listă, cu corecția de la punctul a) deja aplicată, astfel încât să rămână doar sumele
utile. (ex: correctHandValue [A,A,J,C9] => Just (Only 21)) (5p)

Soluție (pt punctul 2 am adăugat și clasa Eq la deriving):


correctValue :: Value -> Maybe Value
correctValue (Only s)
| s > 21 = Nothing
| otherwise = Just (Only s)
correctValue (Either s1 s2)
| s1 > 21 && s2 > 21 = Nothing
| s1 > 21 = Just (Only s2)
| s2 > 21 = Just (Only s1)
| otherwise = Just (Either s1 s2)

handValue :: [Card] -> Value


handValue cards =
let aces = length $ filter (== A) cards
others = filter (/= A) cards
valueOthers = sum [ v | c <- others, let Only v = value c ]
in if aces == 0 then Only valueOthers
else Either (aces + valueOthers) (aces + 10 + valueOthers)

correctHandValue :: [Card] -> Maybe Value


correctHandValue = correctValue . handValue

Barem:
a) 2p - Only s, s > 21 (peste tot, cele 2p se împart: pattern 0.5, condiție 0.5,
2p - Only s, altfel retur 1 (din care 0.5 pt aplicare Just, acolo unde este cazul))
2p - Either, ambele > 21
2p - Either, una > 21
2p - Either, ambele bune
b) 3p - valoare mână goală (dacă se folosește pattern matching, punctele se împart similar cu a))
6p - valoare mână fără ași
6p - valoare mână cu ași
c) 5p – compunerea funcțiilor de mai sus (din care 2p ordinea corectă)
1.
a)
Obs: În rezolvarea acestui subiect, puteți folosi semnul \
(backslash) în loc de λ (lambda).

În Calcul Lambda se definesc:


0 := λf.λx.x
SUCC := λn.λf.λx.(f ((n f) x))

Calculați (SUCC 0).

b)
Obs: În rezolvarea acestui subiect, puteți folosi semnul \
(backslash) în loc de λ (lambda).

În Calcul Lambda se definesc:


1 := λf.λx.(f x)
SUCC := λn.λf.λx.(f ((n f) x))

Calculați (SUCC 1).

c)
Obs: În rezolvarea acestui subiect, puteți folosi semnul \
(backslash) în loc de λ (lambda).

În Calcul Lambda se definesc:


2 := λf.λx.(f (f x))
SUCC := λn.λf.λx.(f ((n f) x))

Calculați (SUCC 2).

d)
Obs: În rezolvarea acestui subiect, puteți folosi semnul \
(backslash) în loc de λ (lambda).

În Calcul Lambda se definesc:


3 := λf.λx.(f (f (f x)))
SUCC := λn.λf.λx.(f ((n f) x))

Calculați (SUCC 3).

Soluții 1:
Barem:
- reducere λn.restul: 4p
- reducere λf interior: 3p
- reducere λx interior: 3p
Primul pas trebuie efectuat separat. În caz contrar, reducerea nu se
punctează.
Dacă pasul 2 este combinat cu pasul 3, se scad 2 puncte.

(SUCC 0)
(λn.λf.λx.(f ((n f) x)) λf.λx.x) ->b
λf.λx.(f ((λf.λx.x f) x)) ->b
λf.λx.(f (λx.x x)) ->b
λf.λx.(f x)

(SUCC 1)
(λn.λf.λx.(f ((n f) x)) λf.λx.(f x)) ->b
λf.λx.(f ((λf.λx.(f x) f) x)) ->b
λf.λx.(f (λx.(f x) x)) ->b
λf.λx.(f (f x))

(SUCC 2)
(λn.λf.λx.(f ((n f) x)) λf.λx.(f (f x))) ->b
λf.λx.(f ((λf.λx.(f (f x)) f) x)) ->b
λf.λx.(f (λx.(f (f x)) x)) ->b
λf.λx.(f (f (f x)))

(SUCC 3)
(λn.λf.λx.(f ((n f) x)) λf.λx.(f (f (f x)))) ->b
λf.λx.(f ((λf.λx.(f (f (f x))) f) x)) ->b
λf.λx.(f (λx.(f (f (f x))) x)) ->b
λf.λx.(f (f (f (f x))))

---------------------------------------------------------------------
---------------------------------
2.
a)
Pentru funcția f de mai jos:
(define (f L)
(if (or (null? L) (null? (cdr L)))
L
(cons (max (car L) (cadr L))
(f (cddr L)))))
i) Argumentați ce tip de recursivitate (stivă/coadă) folosește f.
(1p tipul, 2p argumentația)
ii) Spuneți în cuvinte ce calculează f. (2p)
iii) Definiți funcția ff, care are același efect cu f, dar folosește
celălalt tip de recursivitate. (5p)
Soluție 2a:
i) stivă - apelul recursiv nu e în poziție finală.
ii) se parcurg elementele 2 câte 2 și se reține maximul din fiecare
pereche; pentru o listă cu număr impar de elemente, ultimul element
este copiat ca atare în rezultat.
iii)

; nu se acordă puncte dacă recursivitatea nu este pe coadă


(define (ff L)
(let loop ((L L) (acc '())) ; 1p acc
(if (or (null? L) (null? (cdr L)))
(reverse (append L acc)) ; 1p ordinea corectă
; 1p returul acc pe cazul de bază
(loop (cddr L) (cons (max (car L) (cadr L)) acc)))))
; 2p apelul recursiv cu actualizarea acc

b)
Pentru funcția f de mai jos:
(define (f L)
(cond ((or (null? L) (null? (cdr L))) L)
((equal? (car L) (cadr L)) (f (cdr L)))
(else (cons (car L) (f (cdr L))))))
i) Argumentați ce tip de recursivitate (stivă/coadă) folosește f.
(1p tipul, 2p argumentația)
ii) Spuneți în cuvinte ce calculează f. (2p)
iii) Definiți funcția ff, care are același efect cu f, dar folosește
celălalt tip de recursivitate. (5p)

Soluție 2b:
i) stivă - al doilea apel recursiv nu e în poziție finală.
ii) se elimină duplicatele consecutive din listă.
iii)

; nu se acordă puncte dacă recursivitatea nu este pe coadă


(define (ff L)
(let loop ((L L) (acc '())) ; 1p acc
(cond ((or (null? L) (null? (cdr L))) (reverse (append L acc)))
; 1p ordinea corectă
; 1p returul acc pe cazul de bază
((equal? (car L) (cadr L)) (loop (cdr L) acc))
; 1p apelul cu acc nemodificat
(else (loop (cdr L) (cons (car L) acc))))))
; 1p apelul cu acc actualizat

c)
Pentru funcția f de mai jos:
(define (f L x)
(cond ((null? L) x)
((null? (cdr L)) (+ x (car L)))
((equal? (car L) (cadr L)) (f (cdr L) x))
(else (f (cdr L) (+ x (car L))))))
i) Argumentați ce tip de recursivitate (stivă/coadă) folosește f.
(1p tipul, 2p argumentația)
ii) Spuneți în cuvinte ce calculează f. (2p)
iii) Definiți funcția ff, care are același efect cu f, dar folosește
celălalt tip de recursivitate. (5p)

Soluție 2c:
i) coadă - toate apelurile recursive sunt în poziție finală.
ii) se adună elementele din listă, pentru duplicatele consecutive
luându-se în calcul o singură valoare.
iii)

; nu se acordă puncte dacă recursivitatea nu este pe stivă


(define (ff L)
(cond ((null? L) 0) ; 1p retur pt lista vidă
((null? (cdr L)) (car L)) ; 1p retur pt lista de un element
((equal? (car L) (cadr L)) (ff (cdr L)))

; 1p apelul recursiv cu ignorarea elementului curent


(else (+ (car L) (ff (cdr L))))))
; 2p adunarea (car L) la apelul recursiv

d)
Pentru funcția f de mai jos:
(define (f L x)
(cond ((null? L) x)
((> (car L) x) (f (cdr L) x))
(else (+ (car L) (f (cdr L) x)))))
i) Argumentați ce tip de recursivitate (stivă/coadă) folosește f.
(1p tipul, 2p argumentația)
ii) Spuneți în cuvinte ce calculează f. (2p)
iii) Definiți funcția ff, care are același efect cu f, dar folosește
celălalt tip de recursivitate. (5p)

Soluție 2d:
i) stivă - al doilea apel recursiv nu e în poziție finală.
ii) se adună o valoare x cu toate elementele din listă mai mici sau
egale cu x.
iii)

; nu se acordă puncte dacă recursivitatea nu este pe stivă


(define (ff L x)
(let loop ((L L) (acc x)) ; 1p acc
(cond ((null? L) acc) ; 1p returul acc pe cazul de bază
((> (car L) x) (loop (cdr L) acc))
; 1p apelul cu acc nemodificat
(else (loop (cdr L) (+ acc (car L)))))))
; 2p apelul cu acc actualizat
---------------------------------------------------------------------
---------------------------------
3.
a)
Atenție: Acest exercițiu nu se punctează decât dacă este rezolvat
fără a folosi recursivitate
explicită (deci nici letrec sau named let). Soluțiile explicit
recursive se punctează cu 0.

Definiți, în Racket, funcția odd-pos care selectează doar elementele


aflate pe poziții impare într-o listă L.
Lista este indexată de la 0.
Vă poate fi de folos (dar nu este neapărat necesară) funcția range
(ex: (range 5) => '(0 1 2 3 4) ; (range 8) => '(0 1 2 3 4 5 6 7)).

ex:
(odd-pos '(5 6 5 4 3 4 5)) => '(6 4 4)

Soluție 3a:
; cu map + filter
(define (odd-pos L)
(let ((p (map cons L (range (length L)))))
; 4p împerechere element-poziție (2p folosire map, nu un zip
inventat)
(map car ; 2p extragerea elementelor listei din perechi
(filter (λ(x) (odd? (cdr x))) p))))
; 4p filtrarea pozițiilor impare (2p predicat corect)

; cu fold
(define (odd-pos2 L)
(reverse ; 2p ordine corectă
(second ; 2p rezultat corect (doar elementele cerute, fără alte
informații)
(foldl (λ (x acc)
; 1p ordine corectă pt argumentele funcției de la fold
(if (first acc)
; 2p metodă de a distinge între pozițiile pare și impare
(list #f (cons x (second acc)))

; 1p noul acc pe cazul de poziție impară


(list #t (second acc))))
; 1p noul acc pe cazul de poziție pară
(list #f '())
; 1p acc inițial
L))))

b)
Atenție: Acest exercițiu nu se punctează decât dacă este rezolvat
fără a folosi recursivitate
explicită (deci nici letrec sau named let). Soluțiile explicit
recursive se punctează cu 0.

Definiți, în Racket, funcția alternate care primește o listă L și 2


funcții f1 și f2 și aplică alternativ f1 și f2 pe elementele lui L.
Vă poate fi de folos (dar nu este neapărat necesară) funcția
make-list
(ex: (make-list 4 +) => '(#<procedure:+> #<procedure:+>
#<procedure:+> #<procedure:+>))

ex:
(alternate '(1 2 3 4 5 6 7) add1 sub1) => '(2 1 4 3 6 5 8)

Soluție 3b:
; cu map
(define (alternate L f1 f2)
(let* ((l (length L))
(fL (flatten (make-list l (list f1 f2)))))
; 4p crearea listei de funcții (2p aplatizare cu flatten sau apply)
(map (λ (f x) (f x)) (take fL l) L)))
; 4p aplicarea listei de funcții pe L
; 2p lungimea corectă a listei de funcții

; cu fold
(define (alternate2 L f1 f2)
(reverse ; 2p ordine corectă
(second ; 2p rezultat corect (doar elementele cerute, fără alte
informații)
(foldl (λ (x acc)
; 1p ordine corectă pt argumentele funcției de la fold
(if (first acc)
; 2p metodă de a distinge între pozițiile pare și impare
(list #f (cons (f1 x) (second acc)))
; 1p noul acc pe cazul aplicării f1
(list #t (cons (f2 x) (second acc)))))
; 1p noul acc pe cazul aplicării f2
(list #t '())
; 1p acc inițial
L))))

c)
Atenție: Acest exercițiu nu se punctează decât dacă este rezolvat
fără a folosi recursivitate
explicită (deci nici letrec sau named let). Soluțiile explicit
recursive se punctează cu 0.

Definiți, în Racket, funcția compare-seq care primește o listă


numerică L și întoarce o listă de elemente de tip 'LT, 'EQ sau 'GT
după relația de ordine între elementele consecutive ale listei L ('LT
atunci când primul e mai mic decât al doilea, 'EQ când sunt egale,
'GT când primul e mai mare).

ex:
(compare-seq '(1 1 2 1 4 3)) => '(EQ LT GT LT GT) (explicație: primul
EQ descrie relația dintre elementele de pe primele 2 poziții, apoi LT
pentru elementele de pe pozițiile 2 și 3, apoi GT pentru elementele
de pe pozițiile 3 și 4, etc.)

Soluție 3c:
(define (compare-seq L)
(let* ((l (length L))
(L1 (take L (- l 1)))
; 2p crearea listei fără ultimul element
(L2 (cdr L)))
; 2p crearea listei fără primul element
; 4p aplicarea funcției binare pe cele 2 liste
; 2p funcția binară a lui map
(map (λ (x y) (cond ((= x y) 'EQ)
((< x y) 'LT)
((> x y) 'GT))) L1 L2)))

d)
Atenție: Acest exercițiu nu se punctează decât dacă este rezolvat
fără a folosi recursivitate
explicită (deci nici letrec sau named let). Soluțiile explicit
recursive se punctează cu 0.

Definiți, în Racket, funcția eq-sums? care primește o listă numerică


L cu număr par de elemente și întoarce true dacă toate sumele
provenite din adunarea elementelor de pe poziții simetrice față de
mijlocul listei sunt egale, respectiv false în caz contrar.

ex:
(eq-sums? '(1 4 2 5 3 6)) => #t (explicație: 1+6 = 4+3 = 2+5)
(eq-sums? '(1 4 5 1 3 6)) => #f (explicație: 1+6 = 4+3 != 5+1)

Soluție 3d:
(define (eq-sums? L)
(or (null? L) ; 0p cazul de listă vidă
(let ((R (reverse L))) ; 2p inversarea listei
(null? (filter (λ (x) (not (equal? x (+ (car L) (car R)))))
; 2p predicat corect filter
; 2p prelucrare filter (aici null?)
(map + L R))))))
; 4p adunarea celor 2 liste

Subiectul 4
===========

Varianta 1
----------

Pornind de la fluxul numerelor naturale din Racket, definiți fluxul


fluxurilor de numere naturale consecutive, din 2 în 2, din 3 în 3
ș.a.m.d. Exemplu: ((0 1 2 3 ...) (0 2 4 6 ...) (0 3 6 9 ...) ...).
Considerați că naturals este deja definit și că puteți utiliza orice
funcționale pe fluxuri, discutate la curs și laborator.

Hint: În loc să construiți de la zero fiecare flux interior,


gândiți-vă cum îl puteți obține pe baza fluxului anterior.

Rezolvare:

(define multiples
(stream-cons naturals
(stream-map (λ (s) (stream-zip-with + s naturals))
multiples)))
Barem:

5p: mecanismul de generare a întregului flux (explicit sau implicit)


5p: definirea elementului nou pe baza celui vechi

Varianta 2
----------

Definiți în Racket următorul flux infinit: ((1) (1 2 1) (1 2 3 2 1)


(1 2 3 4 3 2 1) ...).

Hint: În loc să construiți de la zero fiecare listă interioară,


gândiți-vă cum o puteți obține pe baza listei anterioare.

Rezolvare:

(define pyramids
(stream-cons '(1)
(stream-map (λ (L)
(append '(1) (map add1 L) '(1)))
pyramids)))

Barem:

5p: mecanismul de generare a întregului flux (explicit sau implicit)


5p: definirea elementului nou pe baza celui vechi

Varianta 3
----------

Pornind de la un flux infinit de liste, construiți fluxul reuniunilor


parțiale ale acestuia, în care lista de pe poziția i conține
reuniunea primelor i liste din fluxul inițial, iar prima listă este
vidă. Atenție! Reuniunile NU trebuie să conțină duplicate! Exemplu:
pentru fluxul ((2 3) (3 4 5) (2 4) (1) ...) se obține fluxul (() (2
3) (2 3 4 5) (2 3 4 5) (2 3 4 5 1) ...).

Hint: Exploatați faptul că fluxurile de față sunt infinite, pentru a


prelucra simultan fluxul rezultat cu cel inițial.

Rezolvare:
(define (partial-unions sets)
(letrec
([out
(stream-cons
'()
(stream-zip-with (λ (union set)
(append union
(filter (λ (x)
(not (member x union)))
set)))
out
sets))])
out))

Barem:

5p: mecanismul de generare a întregului flux (explicit sau implicit)


5p: definirea elementului nou pe baza celui vechi, din care:
2p: reuniunea
3p: absența duplicatelor

Varianta 4
----------

Fie programul Haskell de mai jos:

lst = [(1+2, 3+4), (3+4, 5+6), (5+6, 7+8)]

find ((7,x) : _) = x
find (_ : pairs) = find pairs

Descrieți cum decurge, pas cu pas, evaluarea expresiei (find lst).

Rezolvare:

find lst
find ((1+2, 3+4) : ...)
find ((3, 3+4) : ...)
find ((3+4, 5+6) : ...)
find ((7, 5+6) : ...)
5+6
11

Barem:
2p: întâi se evaluează doar 1+2 la 3
2p: apoi se evaluează doar 3+4 la 7
2p: se extrage 5+6
2p: se evaluează 5+6 la 11
2p: nu se evaluează altceva

Subiectul 5
===========

Varianta 1
----------

Sintetizați, pas cu pas, tipul următoarei funcții în Haskell:

f x y = head x y

Atenție! Simpla mențiune a tipului final, fără pași intermediari, nu


va fi punctată!

Rezolvare:

f :: a -> b -> c
x :: a
y :: b
head :: [d] -> d
a = [d]
head x = d
d = e -> g
e = b
c = g
f :: [b -> g] -> b -> g

Barem

0p pentru simpla scriere a tipului final, fără niciun pas


intermediar.
Depunctări dacă nu rezultă cum s-a ajuns de la un pas la altul.

Varianta 2
----------
Sintetizați, pas cu pas, tipul următoarei funcții în Haskell:

f x y = fst x : map (snd x) y

Atenție! Simpla mențiune a tipului final, fără pași intermediari, nu


va fi punctată!

Rezolvare:

f :: a -> b -> c
x :: a
y :: b
fst :: (d, e) -> d
snd :: (d, e) -> e
a = (d, e)
fst x :: d
snd x :: e
map :: (g -> h) -> [g] -> [h]
e = g -> h
b = [g]
c = [h]
d = h
f :: (h, g -> h) -> [g] -> [h]

Notă: Am folosit aceleași variable de tip la fst și snd pentru


concizie, întrucât se aplică asupra aceluiași parametru, x. În mod
normal, se utilizează alte variabile de tip, e.g. snd :: (g, h) -> h.

Barem

0p pentru simpla scriere a tipului final, fără niciun pas


intermediar.
Depunctări dacă nu rezultă cum s-a ajuns de la un pas la altul.

Varianta 3
----------

Sintetizați, pas cu pas, tipul următoarei funcții în Haskell:

f x y = [tail x ++ fst y, snd y]

Atenție! Simpla mențiune a tipului final, fără pași intermediari, nu


va fi punctată!
Rezolvare:

f :: a -> b -> c
x :: a
y :: b
tail :: [d] -> [d]
a = [d]
tail x :: [d]
fst :: (e, g) -> e
snd :: (e, g) -> g
b = (e, g)
fst y :: e
snd y :: g
e = [d]
g = [d] (din aceeași listă cu elemente de tipul e)
f :: [d] -> ([d], [d]) -> [[d]]

Notă: Am folosit aceleași variable de tip la fst și snd pentru


concizie, întrucât se aplică asupra aceluiași parametru, y. În mod
normal, se utilizează alte variabile de tip, e.g. snd :: (h, i) -> i.

Barem

0p pentru simpla scriere a tipului final, fără niciun pas


intermediar.
Depunctări dacă nu rezultă cum s-a ajuns de la un pas la altul.

Varianta 4
----------

Sintetizați, pas cu pas, tipul următoarei funcții în Haskell:

f x y = (f x, f y)

Atenție! Simpla mențiune a tipului final, fără pași intermediari, nu


va fi punctată!

Rezolvare:

f :: a -> b -> c
x :: a
y :: b
a = b
f x :: a -> c
f y :: a -> c
c = (a -> c, a -> c)
Eroare de sinteză, întrucât expresia de tip din dreapta conține
strict variabila c.

Barem

0p pentru simpla mențiune a erorii, fără niciun pas intermediar.


Depunctări dacă nu rezultă cum s-a ajuns de la un pas la altul.

Subiectul 6
===========

Varianta 1
----------

Supraîncărcați în Haskell reprezentarea sub formă de șir de caractere


pentru funcții unare, cu parametru numeric, astfel încât o astfel de
funcție să fie vizualizată printr-o listă de perechi, care conțin pe
prima poziție numerele de la 0 la 5, iar pe a doua, valorile funcției
în aceste puncte. De exemplu, show (*2) =
"[(0,0),(1,2),(2,4),(3,6),(4,8),(5,10)]".

Atenție la constrângerile necesare la instanțierea clasei!

Rezolvare:

instance (Num a, Show a, Enum a, Show b) => Show (a -> b) where


show f = show $ map (\x -> (x, f x)) [0..5]

Barem:

1p Show (a -> b)
1p Num a
1p Show a
1p Show b
6p implementare show
4p valorile funcției
2p punctele efective

Nu se depunctează absența constrângerii (Enum a).


Varianta 2
----------

Supraîncărcați în Haskell operatorul de ordonare a funcțiilor unare,


cu parametru numeric, astfel încât funcția f este mai mică sau egală
cu funcția g dacă valorile lui f în punctele 0..100 sunt mai mici sau
egale cu valorile lui g în aceleași puncte. Comparațiile se fac
pentru aceiași parametri. De exemplu, (+1) <= (+2), întrucât (+1) 0
<= (+2) 0, (+1) 1 <= (+2) 1 etc.

Atenție la constrângerile necesare la instanțierea clasei!

Rezolvare:

instance (Num a, Enum a, Ord b) => Ord (a -> b) where


f <= g = and $ zipWith (<=) (map f [0..100]) (map g [0..100])

Barem:

1p Ord (a -> b)
1p Num a
1p Ord b
7p implementare (<=)
2p aplicare f pe cele 100 de puncte
2p aplicare g pe cele 100 de puncte
3p comparare valoare cu valoare

Nu se depunctează absența constrângerii (Enum a).

Varianta 3
----------

Scrieți în Haskell o posibilă instanță a clasei de mai jos, conținând


o implementare neconstantă a funcției convert.

class Convertor c where


convert :: Num a => [a] -> c a

Rezolvare:

instance Convertor Maybe where


convert [] = Nothing
convert xs = Just $ sum xs
Barem:

0p pentru implementare constantă a lui convert.


Nu e obligatorie instanțierea cu Maybe; se poate de exemplu și cu [].

Varianta 4
----------

Scrieți în Haskell o posibilă instanță a clasei de mai jos, conținând


o implementare neconstantă a funcției merge.

class Merger m where


merge :: m a -> m a -> m [a]

Rezolvare:

instance Merger Maybe where


merge (Just x) (Just y) = Just [x, y]
merge _ _ = Nothing

Barem:

0p pentru implementare constantă a lui convert.


Nu e obligatorie instanțierea cu Maybe; se poate de exemplu și cu [].

Subiectul 7
===========

Varianta A
----------

Avem premisele, date în formă clauzală:

{ ¬P(x, y, z), ¬Q(z, x), R(x, y) }


{ P(Ion, Test, George) }
{ Q(George, Ion) }

Demonstrați *folosind rezoluția* că este adevărat R(Ion, Test).


Soluție:

Sunt 3 pași de rezoluție, cu P, Q, și R din prima clauză.

De exemplu:

¬R(Ion, Test) // concluzia negată


¬P(x, y, z) V ¬Q(z, x) V R(x, y)
------------------------------ x <- Ion, y <- Test
¬P(Ion, Test, z) V ¬Q(z, Ion)
Q(George, Ion)
------------------------------ z <- George
¬P(Ion, Test, George)
P(Ion, Test, George)
------------------------
clauza vidă

Barem:

+3p per pas de rezoluție, -1p la fiecare pas dacă lipsește


substituția sau este incorectă
+1p pentru imaginea de ansamblu și finalizarea corectă

Varianta B
----------

Avem premisele, date în formă clauzală:

{ ¬P(x, y), ¬Q(x, y) }


{ ¬R(x, y), Q(x, y) }
{ P(x, y), R(x, y) }
{ P(Armand, Cadou) }
{ ¬P(Claudia, Cadou) }

Demonstrați *folosind rezoluția* că este adevărat Q(Claudia, Cadou).

Soluție:

Sunt 3 pași necesari, pentru


¬Q(Claudia, Cadou)
¬R(x, y), Q(x, y)
P(x, y), R(x, y)
¬P(Claudia, Cadou)

De exemplu:

¬Q(Claudia, Cadou) // concluzia negată


¬R(x, y) V Q(x, y)
------------------- x <- Claudia, y <- Cadou
¬R(Claudia, Cadou)
P(x, y) V R(x, y)
------------------- x <- Claudia, y <- Cadou
P(Claudia, Cadou)
¬P(Claudia, Cadou)
-------------------
clauza vidă

Barem:

+3p per pas de rezoluție, -1p la fiecare pas dacă lipsește


substituția sau este incorectă
+1p pentru imaginea de ansamblu și finalizarea corectă

Varianta C
----------

Avem premisele, date în formă clauzală:

{ ¬P(x, y), ¬Q(z, t, x), R(t, z, y) }


{ Q(Ionuț, Aurel, Carioca) }
{ P(Carioca, Pachet) }

Demonstrați *folosind rezoluția* că este adevărat R(Aurel, Ionuț,


Pachet).

Soluție:

Sunt 3 pași de rezoluție, cu P, Q, și R din prima clauză.

De exemplu:
¬P(x, y) V ¬Q(z, t, x) V R(t, z, y)
¬R(Aurel, Ionuț, Pachet) // concluzia negată
---------------------------- t <- Aurel, z <- Ionuț, y <- Pachet
¬P(x, Pachet) V ¬Q(Ionuț, Aurel, x)
Q(Ionuț, Aurel, Carioca)
--------------------------- x <- Carioca
¬P(Carioca, Pachet)
P(Carioca, Pachet)
-------------------
clauza vidă

Barem:

+3p per pas de rezoluție, -1p la fiecare pas dacă lipsește


substituția sau este incorectă
+1p pentru imaginea de ansamblu și finalizarea corectă

Varianta D
----------

Avem premisele, date în formă clauzală:

{ ¬P(x), ¬Q(y, x), ¬T(z), ¬R(z, x) }


{ P(Cadou) }
{ Q(12, Cadou) }
{ T(21) }

Demonstrați *folosind rezoluția* că este adevărat ¬R(21, Cadou).

Soluție:

Sunt 4 pași de rezoluție, cu P, Q, T și R din prima clauză.

De exemplu:

¬P(x) V ¬Q(y, x) V ¬T(z) V ¬R(z, x)


Q(12, Cadou)
--------------------------- x <- Cadou, y <- 12
¬P(Cadou) V ¬T(z) V ¬R(z, Cadou)
P(Cadou)
------------------------
¬T(z) V ¬R(z, Cadou)
T(21)
--------------------- z <- 21
¬R(12, Cadou)
R(21, Cadou) // concluzia negată
--------------
Clauza vidă

Barem:

+2p per pas de rezoluție, -1p la fiecare pas dacă lipsește


substituția sau este incorectă
+2p pentru imaginea de ansamblu și finalizarea corectă

Subiectul 8
===========

Se respectă forma răspunsului 1p


Caz de bază 1p
Ordinea raspunsurilor 2p
Corectitudinea 6p
Mai multe soluții -3p
Cut neexplicat sau plasat nediscriminatoriu -2p
Singletons -1p

Varianta A
----------

Implementați în Prolog predicatul identify(+ListsIn, -ListsOut) care


pentru o listă de liste numerice, produce o listă de perechi, fiecare
pereche (N, L) corespunzând unei liste L din ListsIn, iar numărul N
fiind:
- maximul listei, dacă L este sortată crescător;
- primul element din L, altfel.

Notă: listele din ListsIn nu conțin duplicate.


Hint: puteți folosi predicatul predefinit sort/2, sort(+List,
-Sorted).
De exemplu, interogarea identify([[1,2,3], [2,1,3], [4,5,6],
[7,6,9]], X).
este adevărată pentru X = [(3, [1, 2, 3]), (2, [2, 1, 3]), (6, [4,
5, 6]), (7, [7, 6, 9])].

Puteți folosi predicate ajutătoare. Predicatul trebuie să întoarcă


cel mult o soluție. Dacă ați folosit predicatul cut, explicați
plasarea acestuia. Nu lăsați variabile singleton.

Soluție:

identify([], []).
identify([L|LL], [(H, L) | LLOut]) :-
sort(L, L), !, reverse(L, [H|_]), identify(LL, LLOut).
identify([[H|T]|LL], [(H,[H|T])|LLOut]) :- identify(LL, LLOut).

Varianta B
----------

Implementați în Prolog predicatul noRedundancies(+ListsIn,


-ListsOut). ListsIn este o listă de liste. ListsOut este o sublistă
din ListsIn. Ne interesează să păstrăm la ieșire doar liste care
conțin cel puțin un element nou față de listele mai din stânga.

Hint: puteți folosi predicatul predefinit subset/2, subset(+SubSet,


+Set).

De exemplu, interogarea noRedundancies([[3,1], [2,5,6,7], [4,5,6],


[1,2,3]], X). va face legarea:
X = [[3, 1], [2, 5, 6, 7], [4, 5, 6]].
pentru că toate elementele din lista [1,2,3] sunt deja conținute în
liste mai din stânga.

Iar interogarea noRedundancies([[5,6,7,3], [4,5], [4,5,6], [1,2],


[1,3,2]], X).
va face legarea:
X = [[5, 6, 7, 3], [4, 5], [1, 2]].
pentru că nici [1,2,3], nici [4,5,6] nu conțin elemente noi față de
listele mai din stânga lor.
Puteți folosi predicate ajutătoare. Predicatul trebuie să întoarcă
cel mult o soluție. Dacă ați folosit predicatul cut, explicați
plasarea acestuia. Nu lăsați variabile singleton.

Soluție:

aux3b([], [], _).


aux3b([L|LL], LLOut, Elements) :-
subset(L, Elements), !,
aux3b(LL, LLOut, Elements).
aux3b([L|LL], [L|LLOut], Elements) :-
append(Elements, L, Elements1),
aux3b(LL, LLOut, Elements1).
noRedundanciesB(LL, LLOut) :- aux3b(LL, LLOut, []).

Varianta C
----------

Implementați în Prolog predicatul subDown(+List, -Sub) care leagă Sub


la o sublistă descrescătoare a lui List (care este nevidă), conținând
următoarele elemente:
- primul E1 element din List
- următorul element Ei mai mic decât E1
- următorul element Ej mai mic decât Ei
- și așa mai departe.

De exemplu, sunt adevărate:


subDown([5,1,2,3,0,4,3, 6], X). cu X = [5, 1, 0]
subDown([1,2,0,3,4,3], X). cu X = [1, 0]
subDown([1,2,3,4], X). cu X = [1]

Puteți folosi predicate ajutătoare. Predicatul trebuie să întoarcă


cel mult o soluție. Dacă ați folosit predicatul cut, explicați
plasarea acestuia. Nu lăsați variabile singleton.

Soluție:

aux2([], _, []).
aux2([H|T], C, [H|ST]) :- H < C, !, aux2(T, H, ST).
aux2([_|T], C, ST) :- aux2(T, C, ST).
subDown([H|T], [H|ST]) :- aux2(T, H, ST).
Varianta D
----------

Implementați în Prolog predicatul subUp(+List, -Sub) care leagă Sub


la o sublistă crescătoare a lui List (care este nevidă), conținând
următoarele elemente:
- primul E1 element din List
- următorul element Ei mai mare decât E1
- următorul element Ej mai mare decât Ei
- și așa mai departe.

De exemplu, sunt adevărate:


subUp([1,2,0,3,4,3], X). cu X = [1, 2, 3, 4]
subUp([5,1,2,3,4,3, 6], X). cu X = [5, 6]
subUp([5,1,2,3,4,3], X). cu X = [5]

Puteți folosi predicate ajutătoare. Predicatul trebuie să întoarcă


cel mult o soluție. Dacă ați folosit predicatul cut, explicați
plasarea acestuia. Nu lăsați variabile singleton.

Soluție:

aux([], _, []).
aux([H|T], C, [H|ST]) :- H > C, !, aux(T, H, ST).
aux([_|T], C, ST) :- aux(T, C, ST).
subUp([H|T], [H|ST]) :- aux(T, H, ST).

Subiectul 9
===========

Varianta A
----------
Implementați folosind metapredicate (forall, findall, bagof, setof),
și fără recursivitate explicită, predicatul counts(+List, -ListPlus),
care primește în List o listă de liste de elemente oarecare, și leagă
ListPlus o listă de liste, fiecare element LP din ListPlus fiind
bazat pe o listă L din List: primul element este numărul elementelor
*diferite* din L, iar restul elementelor sunt elementele din L.

Exemplu:

counts([[a], [1, 2, 3, 4, 3], [x, y, z], [d, 1, d, 1], [e,


1,1,1,1,1,1]], X) leagă X la
[[1,a],[4,1,2,3,4,3],[3,x,y,z],[2,d,1,d,1],[2,e,1,1,1,1,1,1]].

Sol:

counts(List, ListPlus) :-
findall([Len|L],
(
member(L, List),
setof(E, member(E, L), Es),
length(Es, Len)
),
ListPlus).

Barem:
Numărare elemente unice: 3p
Aplicare mecanism numărare elemente unice pe fiecare listă: 4p
Asamblare corectă: 3p

Varianta B
----------

Implementați folosind metapredicate (forall, findall, bagof, setof),


și fără recursivitate explicită, predicatul heads(+List, +Limit,
-Heads) care primește o listă de liste, fiecare listă fiind formată
dintr-un prim element și restul listei (restul poate fi și vid).
Predicatul leagă Heads la lista acelor prime elemente în ale căror
resturi de liste există cel puțin Limit elemente *diferite*.

Exemplu:
Pentru lista List = [[a], [b, 1, 2, 3, 4, 3], [c, x, y, x, y], [d,
1], [e, 1,1,1,1,1,1,1,1]]

heads(List, 2, X) leagă X la [b, c] pentru că doar listele cu b și cu


c au cel puțin 2 elemente diferite, iar
heads(List, 4, X) leagă X la [b] pentru că în lista lui c sunt doar 2
elemente diferite.

Sol:

heads(List, Limit, Heads) :-


findall(X,
(
member([X|R], List),
setof(E, member(E, R), L),
length(L, Len), Len >= Limit
),
Heads).

Barem:
Găsire elemente unice: 4p
Verificarea numărului de elemente unice: 2p
Asamblarea soluției: 3p

Varianta C
----------

Implementați folosind metapredicate (forall, findall, bagof, setof),


și fără recursivitate explicită, predicatul index(+List, -Index) care
primește o listă de elemente și întoarce o listă de perechi, fiecare
pereche fiind formată dintr-un element E din List și numărul de
apariții ale lui E în List. Hint: în Index elementele apar sortate.

Exemplu:
index([8, 1, 2, 3, 1, 2, 5, 4, 7, 6, 2, 5], X) leagă X la [(1, 2),
(2, 3), (3, 1), (4, 1), (5, 2), (6, 1), (7, 1), (8, 1)]

Sol:
index(List, Index) :-
setof((E, Times),
L^(
member(E, List),
findall(X, (member(X, List), E = X), L),
length(L, Times)
),
Index).

sau

index(List, Index) :-
setof(E, member(E, List), Es),
findall((E, Times),
( member(E, Es),
findall(X, (member(X, List), E = X), L),
length(L, Times)
),
Index).

Barem:
Găsirea elementelor fără duplicate: 4p
Numărul de apariții pentru fiecare element: 2p
Asamblarea soluției: 2p

Varianta D
----------

Implementați folosind metapredicate (forall, findall, bagof, setof),


și fără recursivitate explicită, predicatul singles(+List, -Singles),
care filtrează în Singles elementele din List care apar o singură
dată. Rezultatul este ordonat.

Exemplu:

singles([8,1,2,3,1,2,5,4,7,6,2,5], X) leagă X la [3, 4, 6, 7, 8].

Sol:

singles(List, Singles) :-
setof(E,
L^( member(E, List),
findall(X, (member(X, List), E = X), L),
length(L, 1)
),
Singles).

Barem:
Verificarea dacă un element este unic: 4p
Verificarea unicității pentru toate elementele: 4p
Asamblarea soluției: 2p

S-ar putea să vă placă și