Sunteți pe pagina 1din 131

Racket

;; Funcție în Racket ca zip din Haskell


(define (myzip L1 L2)
(if (null? L1) '()
(append (list (cons (car L1) (car L2))) (myzip (cdr L1) (cdr L2)))))

(define (my-zip1 L1 L2)


(map cons L1 L2))

;; Funcție în Racket ca unzip din Haskell

(define (my-unzip L1)


(cons (map car L1) (map cdr L1)))

;; Element maxin dintr-o lista

(define (getMax L)
(car(sort L >)))

;; MapAnd

(define (MyAndMap L)
(foldl (lambda (a b) (and a b)) #t L))

;; Functie care elimina duplicatele dintr-o lista


(define (remove-duplicates lon)
(foldr (lambda (x y) (cons x (filter (lambda (z) (not (= x z))) y))) empty lon))

;; Intersectia a 2 liste in Racket

(define (intersection1 L1 L2)


(remove-duplicates (append L1 L2))
)

Haskell
-- Tipuri funcții uzuale

map :: (e -> i) -> [e] -> [i]


filter :: (a -> Bool) -> [a] -> [a]
max :: (Ord a) => a -> a -> a
zip :: [a] -> [b] -> [(a, b)]
unzip :: [(a, b)] -> ([a], [b])
fmap :: Functor f => (a -> b) -> f a -> f b
foldr :: (a -> b -> b) -> b -> [a] -> b
foldl :: (b -> a -> b) -> b -> [a] -> b
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
(.) :: (b -> c) -> (a -> b) -> a -> c

-- Exemplu instantiere clasa show din Haskell


instance (Enum a, Num a, Show b) => Show (a -> b) where
show f = concatMap (show . f) [1..10]

-- Exemplu de supraincarcare a operatorului + peste Maybe


instance Num a => Num (Maybe a) where
Just x + Just y = Just $ x + y
_ + _ = Nothing

-- Fluxul [[1,2,3,4,5],[2,4,6,8,10],[3,6,9,12,15],[4,8,12,16,20] ...] în Haskell.


flux = [take 5 [m | m <- [n..], mod m n == 0] | n <- [1..]]

-- Flux de divizori
flux1 = [[d | d <- [1..n], mod d n == 0] | n <- [1..]]

-- Lista circulara ini Haskell cu next

circular l = (concat . repeat) l


get = head
next = tail

-- Funcție care face diferența intre 2 multimi

-- Functie auxiliara member


member e xs
= foldr (||)
False
(map (e ==) xs)

setD mult1 mult2 = [x | x <- mult1, (not (member x mult2))]

Prolog
%% Concatenarea a 2 liste în Prolog

concat1([], L2, L2).


concat1([Head|Tail], L2, [Head|L3]) :- concat (Tail, L2, L3).

%% Intersecția a 2 multimi în Prolog

intersection (A, B, Result) :- findall (X, member(X, A), member(X, B), Result).

A -> B <=> !A sau B

%% Numerele cuprinse într-un interval dintr-o lista

predicat(L, A, B, N) :- findall (X, (member(X, L), X > A, X < B), S), length(S, N).

%% Implementare map f

mapF(L, LO):- findall(XO, (member(X, L), f(X, XO)), LO).


%% Implementarea functiei count în Prolog
count(_, [], 0) :- !.

count(X, [H|T], N) :-
X == H,
count(X, T, N2),
N is N2 + 1.

count(X, [Y|T], N) :-
X \= Y,
count(X, T, N).

Reduceri – reguli practice


λz.(λz.z y)→βλz.y
λz.(λx.y a)→βλz.y (same)
(λy.λx.xΩ)→λx.x
λf.(f (λf.f f)) -> λf.(f f)
λf.f(λf.(f f) f)) -> λf.(f (f f))

Exemplu cu Omega:
(λy.((λx.λy.x y) Ω)λx.λy.y)
Solutie:
(λy.((λx.λy.x y) Ω)λx.λy.y)→((λx.λy.x λx.λy.y) Ω)
→(λy.λx.λy.yΩ)→λx.λy.y

Probleme rezolvate la laborator:

Racket:
Lab 1:
;; Fie următoarele axiome pentru obținerea reprezentării unui număr
natural
;; în baza b (cu [] = lista vidă și ++ =
concatenare).
;; num->base(0,b) = [ ] ; pt n=0
;; num->base(n,b) = num->base(n div b, b) ++ [ n mod b ] ; pt n>0
;; Implementați funcția corespunzătoare în Racket:
(define (num->base n b)
(if (zero? n)
'()
(append (num->base (quotient n b) b) (list (modulo n b)))))
;; Inversul unei liste

(define (rev L)
(if (null? L)
'()
(append (rev (cdr L)) (list (car L)))))

;; Verificam dacă o lista este palindrom

(define (palindrome? L) (equal? L (reverse L)))

;; Testați că n este palindrom în toate bazele din lista


Bases:
(define (all-palindromes? n Bases)
(cond
((null? Bases) #t)
((palindrome? (num->base n (car Bases)))
(all-palindromes? n (cdr Bases)))
(else #f)))

;; Găsiți toate numerele naturale, mai mici sau egale cu n, care


sunt
;; palindroame în toate bazele din lista
Bases:
(define (palindromes-to-n n Bases)
(if (eq? n -1)
'()
(if (all-palindromes? n Bases)
(append (palindromes-to-n (sub1 n) Bases) (list n))
(palindromes-to-n (sub1 n) Bases))))

;; Să se găsească primul număr mai mare decât start care este


palindrom în
;; minimum b baze dintre bazele 2, 3, 4, 5, 6, 7, 8, 9,
10.
(define (count-palindrome-bases n base found target)
(cond
((equal? found target) #t)
((> base 10) #f)
((palindrome? (num->base n base)) (count-palindrome-bases n (add1 base) (add1 found) target))
(else (count-palindrome-bases n (add1 base) found target))))
(define (first-b-pal start b)
(if (count-palindrome-bases (add1 start) 2 0 b)
(add1 start)
(first-b-pal (add1 start) b)))

; Să se găsească cea mai lungă porțiune continuă care este palindrom, dintr-un număr, în baza
10.

(define (num->list n)
(if (zero? n)
'()
(append (num->list (quotient n 10)) (list (modulo n 10)))))
(define (longest-palindrome n)
(if (palindrome? (num->list n))
n
(max (longest-palindrome (modulo n (expt 10 (sub1 (length (num->list n))))))
(longest-palindrome (quotient n 10)))))

Lab 2:
;; Exercitiul 1 - folosirea functiei trace pentru a intelege mai bine desfasurarea recursivitatii
(define (factorial n)
(if (= n 1)
1
(* n (factorial (- n 1)))))

(define (factorial2 n) ;; acest apel produce redefinirea functiei factorial


(fact-iter 1 1 n))
(define (fact-iter product counter max-count)
(if (> counter max-count)
product
(fact-iter (* counter product)
(+ counter 1)
Max-count)))
;; Sirul lui fibonacci

(define (fib n)
(cond ((= n 0) 0)
((= n 1) 1)
(else (+ (fib (- n 1))
(fib (- n 2))))))

(define (fib2 n)
(fib-iter 1 0 n))
(define (fib-iter a b count)
(if (= count 0)
b
(fib-iter (+ a b) a (- count 1))))

;; Suma elementelor dintr-o lista

(define (sum-list L)
(sum-list-iter 0 L))
(define (sum-list-iter sum L)
(if (null? L)
sum
(sum-list-iter (+ sum (car L)) (cdr L))))

;; Suma elementelor dintr-o lista

(define (len L)
(len-iter 0 L))
(define (len-iter result L)
(if (null? L)
result
(len-iter (+ 1 result) (cdr L))))

;; Implementare append
(define (app A B)
(app-iter B (reverse A)))
(define (app-iter B Result)
(if (null? B)
(reverse Result)
(app-iter (cdr B) (cons (car B) Result))))

;; Exercițiul 2 din laboratorul 1 solicita implementarea funcției de inversare,


;; pornind de la următoarele axiome:
;; rev([ ]) = [ ]
;; rev(x:l) = rev(l) ++ [x]
;; Ce tip de recursivitate este surprins aici?

(exercițiul 1 : 1 punct)

(define (rev L)
(let loop ((pend L)
(res '()))
(if (null? pend)
res
(loop (cdr pend) (cons (car pend) res)))))

(check-exp (rev '(5 1 4 8 7)) '(7 8 4 1 5))

(exercițiul 2 : 1.5 puncte)


;; Păstrați dintr-o listă de imagini doar acele imagini care au înălțimea mai mică decât height.
;; Pentru a determina înălțimea unei imagini folosiți funcția image-height.
;; Exemplu: (image-height (ellipse 30 40 "solid" "orange"))
;; Funcția va fi recursivă pe stivă.
(define (lesser images height)
(cond [(empty? images) '()]
[else
(cond [(< (image-height (car images)) height) (append (list (car images)) (lesser (cdr images)
height))]
[else (lesser (cdr images) height)])]))

(exercițiul 3 : 1.5 puncte)


;; Păstrați dintr-o listă de imagini doar acele imagini care au înălțimea mai mare sau egală cu height.
;; Funcția va fi recursivă pe coadă.

(define (greater images height)


(cond [(empty? images) '()]
[else
(cond [(>= (image-height (car images)) height) (append (list (car images)) (greater (cdr
images) height))]
[else (greater (cdr images) height)])]))

(exercițiul 4 : 1.5 puncte)


;; Păstrați dintr-o listă prima apariție a elementelor duplicat.
;; Care este tipul natural de recursivitate pentru această funcție?
;; hint: (member x L) - verifică dacă elementul x este în lista L
;; Exemple: (member 2 '(1 2 3)) -> '(2 3), (member 4 '(1 2 3)) -> #f
;; Funcția member poate fi folosită drept condiție în structura if
(define (remove-duplicates-left L)
(cond
[(empty? L) empty]
[else (cons (first L) (remove-duplicates-left (filter (lambda (x) (not (equal? (first L) x))) L)))])
)
(exercițiul 5 : 1.5 puncte)
;; Păstrați dintr-o listă ultima apariție a elementelor duplicat. Elementele din
;; lista finală vor fi în aceeași ordine.
;; NU folosiți inversarea de liste în rezolvarea exercițiului
;; Care este tipul natural de recursivitate pentru această funcție?

(define (remove-duplicates-right L)
(cond
[(= (length L) 0) '()]
[(if (member (car L) (cdr L)) (remove-duplicates-right (cdr L))
(append (list (car L)) (remove-duplicates-right (cdr L))))]
)
)

(check-exp-part 'a .5 (remove-duplicates-right '(1 2 3 3 4 5 6 3 3)) '(1 2 4 5 6 3))


(check-exp-part 'b .5 (remove-duplicates-right IMAGES) '(. . . . . . . . .))

(exercițiul 6 : 1.5 puncte)


;; Determinați prefixul comun pentru două liste, utilizând o funcție recursivă.
(define (common-prefix a b)
(cond ((null? a) '())
((null? b) '())
((equal? (car a) (car b)) (cons (car a) (common-prefix (cdr a) (cdr b))))
(else '()))
)
(check-exp-part 'a .5 (common-prefix '(1 2 3 4 5 6 7) '(1 2 3 9 10 12 5 6)) '(1 2 3))
(check-exp-part 'b .5 (common-prefix '(. . . . . . .) '(. . . . . . .)) '(. . . .))

(exercițiul 7 : 1.5 puncte)


;; Determinați sufixul comun pentru două liste, utilizând o funcție recursivă.
;; NU este permisă inversarea listelor L1 si L2 sau aplicarea funcției de la punctul (6).
;; Implementarea trebuie să aibă o complexitate mai bună de O(n^2).
;; Ce tip de recursivitate ați folosit pentru realizarea acestei funcții?
;; S-ar fi putut implementa funcția folosind celălalt tip de recursivitate?
;; hint: (drop L n) - elimină primele n elemente din lista L

(define (common-suffix L1 L2)


(if (= (length L1) (length L2))
(if (= 0 (min (length L1) (length L2)))
'()
(if (equal? (car L1) (car L2))
(append (cons (car L1) null) (common-suffix (cdr L1) (cdr L2)))
(append (common-suffix (cdr L1) (cdr L2)) null)))
(if (< (length L1) (length L2))
(common-suffix L1 (cdr L2))
(common-suffix (cdr L1) L2))))

(check-exp-part 'a .25 (common-suffix '(1 2 3 4 5 6 7) '(1 2 3 9 10 12 5 6)) '())


(check-exp-part 'b .25 (common-suffix '(1 2 3 4 5 6 7) '(1 2 3 9 10 12 5 6 7)) '(5 6 7))
(check-exp-part 'c .5 (common-suffix '(. . . . . .) '(. . . . . . .)) '(. . . .))

(exercițiul 8 : 1 punct BONUS)


;; Pornind de la o listă de imagini, și de la o imagine inițială, construiți
;; o unică imagine, rezultată prin suprapunerea tuturor imaginilor, în maniera
;; următoare:
;; * prima imagine din listă deasupra imaginii inițiale
;; * a doua imagine din listă deasupra rezultatului
;; ...
;; * ultima imagine din listă deasupra rezultatului.
;; Numele funcției este 'overlay->', pentru a indica sensul de parcurgere
;; de la stânga la dreapta.
;; Care este tipul natural de recursivitate pentru această funcție?
;; Utilizați funcția predefinită (overlay <deasupra> <dedesubt>).

(define (overlay-> initial images)


(if (null? images) initial
(overlay-> (overlay (car images) initial) (cdr images))
))

(check-exp (overlay-> INITIAL BONUS-IMAGES) .)

(exercițiul 9 : 2 puncte BONUS)


;; Implementați sortarea prin interclasare (merge sort) pentru imagini
;; Sortarea trebuie să fie în ordine descrescătoare, cu funcția de ordine:
;; img1 < img2 = (image-height img1) < (image-height img2)
;; În cazul în care înălțimile imaginilor sunt egale, se va compara lățimea
;; Axiome mergesort:
;; mergesort [x] = [x] - o listă cu un element este deja sortată
;; mergesort L = merge (mergesort (fst-half L)) (mergesort (snd-half L)) unde:
;; - merge = funcție de interclasare a două liste (primește două liste sortate
;; și întoarce o listă sortată compusă din cele două liste primite)
;; - fst-half / snd-half = funcții ce împart o listă în două jumătăți

(define (merge-lists xs ys)


(cond
[(null? xs) ys]

[(null? ys) xs]


[(> (image-height (car xs)) (image-height (car ys)))

(cons (car xs) (merge-lists (cdr xs) ys))]

[
(cons (car ys) (merge-lists xs (cdr ys)))]))

(define (mergesort L)
(cond
[(or (null? L) (null? (cdr L))) L]
[(null? (cddr L))
(merge-lists (list (car L)) (cdr L))]
[#t
(let ([x (ceiling (/ (length L) 2))])
(merge-lists (mergesort (take L x))
(mergesort (drop L x))))]))

Lab 3:

;; Exercitiul 1 - decomentati apelurile de mai jos pentru a


;; observa comportamentul functiilor curry/uncurry
;; exercitiul a fost prezentat si la curs
(define add-curry
(lambda (x)
(lambda (y)
(+ x y))))
((add-curry 2) 5) ;; 7 - observati apelul: intai (add-curry 2), apoi rezultatul aplicat pe 5
(add-curry 2) ;; #<procedure> - va fi o noua functie care asteapta un argument
;; pentru a il aduna cu 2
(define add-uncurry
(lambda (x y)
(+ x y)))
(add-uncurry 2 5) ;; 7
; (add-uncurry 2) ;; procedure add-uncurry: expects 2 arguments, given 1: 2
;; deci o eroare, intrucat o functie uncurry trebuie sa isi primeasca
;; toate argumentele deodata
(define inc-curry (add-curry 1))
(inc-curry 5) ;; 6 - inc-curry este exact functia increment, definita aici cu
;; minim de efort pe baza functiei add-curry
;; ceea ce produce (add-curry 1) este (lambda (y) (+ x y)) cu x legat la 1
(define (inc-uncurry x)
(add-uncurry 1 x))
(inc-uncurry 5) ;; 6 - aceeasi functie increment, insa definita mai greoi (e nevoie de parametru)
;; Exercitiul 2 - mai multa reutilizare de cod, de data asta
;; pentru a particulariza o functie de sortare
;; dupa orice tip de comparator
(define (ins-in-sorted op)
(lambda (e L)
(if (or (null? L) (op e (car L))) ;; op = un operator de comparatie
(cons e L)
(cons (car L) ((ins-in-sorted op) e (cdr L))))))
(define (sort-ins op)
(lambda (L)
(if (null? L)
L
((ins-in-sorted op) (car L) ((sort-ins op) (cdr L))))))
(define sort-ascending (sort-ins <=))
(define sort-descending (sort-ins >=))
(define sort-mod5 (sort-ins (lambda (x y) (< (modulo x 5) (modulo y 5)))))
(sort-ascending '(6 1 7 9 3 2 4 6 7)) ;; o functie de sortare crescatoare
(sort-descending '(6 1 7 9 3 2 4 6 7)) ;; o functie de sortare descrescatoare
(sort-mod5 '(6 1 7 9 3 2 4 6 7)) ;; o functie de sortare dupa restul la impartirea cu 5
;; Observatie: sortarea mod 5 ar putea folosi o functie derivata dintr-o comparatie mod x, cu x
oarecare
;; puteti incerca asta ca exercitiu
;; Exercitiul 3 - implementarea functionalei map
(define (map1 f L)
(if (null? L)
L
(cons (f (car L)) (map1 f (cdr L)))))
(map1 (lambda (x) (* 2 x)) '(1 2 3 4 5))
(map1 (lambda (x) (even? x)) '(1 2 3 4 5))
(map1 (lambda (x) (remainder x 3)) '(4 5 7 8 9 1))
;; Exercitiul 4 - implementarea functionalei filter
(define (filter1 p L)
(cond
((null? L) L)
((p (car L)) (cons (car L) (filter1 p (cdr L))))
(else (filter1 p (cdr L)))))
;(filter1 even? '(1 2 3 4 5 6 7 8))
;(filter1 positive? '(-3 -4 5 6 -8 2 -1))
;; Exercițiul 5 - implementarea funcționalei foldl
(define (foldl1 p acc L)
(if (null? L)
acc
(foldl1 p (p (car L) acc) (cdr L))))
(foldl1 + 0 '(1 2 3 4))
(foldl1 (lambda (x acc) (if (>= x 10) (cons x acc) acc)) null '(1 3 10 11 4 12))
(foldl1 cons null '(1 2 3 4))
;; Exercițiul 6 - implementarea funcționalei foldr
(define (foldr1 p acc L)
(if (null? L)
acc
(p (car L) (foldr1 p acc (cdr L)))))
(foldr1 + 0 '(1 2 3 4))
(foldr1 cons null '(1 2 3 4))
(foldr1 (lambda (x acc) (if (>= x 10) (cons x acc) acc)) null '(1 3 10 11 4 12))
;; Exercitiul 7 - suma elementelor unei liste utilizand apply
(define (sumlist L)
(apply + L))
(sumlist '(1 2 3 4 5))
;; Exercitiul 8 - suma elementelor unei liste utilizand foldr
(define (sumlist2 L)
(foldr + 0 L))
(sumlist2 '(1 2 3 4 5))
;; Exercitiul 9 - inversarea unei liste utilizand foldl
(define (reverselist L)
(foldl cons null L))
(reverselist '(1 2 3 4 5 6))
;; Exercițiul 10 BONUS - implementarea funcționalei apply
;; Pentru mai multe detalii legate de namespace și eval, accesati documentatia de Racket
;; (eval: Racket Documentation -> The Racket Guide -> Reflection and Dynamic Evaluation ->
eval)
;; (namespaces: Racket Documentation -> The Racket Reference -> Language Model -> Syntax
Model -> Namespaces)
(define (apply1 fun args)
(eval (map (lambda (x) (list 'quote x)) (cons fun args)) (make-base-namespace)))
(apply1 + '(1 2 3))
(apply1 cons '(1 2))

(exercițiul 1 : 0.5 puncte)


;; Implementați funcția uncurry->curry astfel:
;; uncurry->curry transformă o funcție uncurry într-o funcție curry
;; (se consideră că funcția e binară)
;; Ex: (((uncurry->curry +) 2) 3) va avea același efect ca (+ 2 3)

(define (uncurry->curry f)
(lambda (x)
(lambda (y)
(f x y))))

(exercițiul 2 : 0.5 puncte)


;; Implementați funcția curry->uncurry astfel:
;; curry->uncurry transformă o funcție curry într-o funcție uncurry
;; (se consideră că funcția e binară)
;; Ex: ((curry->uncurry f) 2 3) va avea același efect ca ((f 2) 3)

(define (curry->uncurry f)
(λ (x y)
((f x) y)
))

(exercițiul 3 : 2 puncte)
;; Reimplementați funcțiile remove-duplicates-left, remove-duplicates-right, overlay-> și overlay<-
;; de la laboratorul 2, fără a folosi explicit recursivitatea, ci utilizând
;; funcționale.
;; Acolo unde este cazul, folosiți funcții anonime.

(define (remove-duplicates-left L)
(foldr (lambda (x y) (cons x (filter (lambda (z) (not (equal? x z))) y))) empty L))

(define (remove-duplicates-right L)
(foldl (lambda (x y) (cons x (filter (lambda (z) (not (equal? x z))) y))) empty L)
)
(define (f matrix)
(if (null? matrix)
'()
(append (list (overlay-> empty-image (car matrix))) (slim-horizontal (cdr matrix)))

(define (slim-horizontal matrix)


(f matrix))

(check-exp (slim-horizontal MATRIX) '(. . .))

(exercițiul 5 : 1.5 puncte)


;; Implementați funcția matrix-to-image care primește o matrice de imagini
;; și întoarce reprezentarea sub formă de imagine a acesteia folosind
;; funcțiile beside (reunește imaginile date ca parametri pe orizontală)
;; și above (reunește imaginile date ca parametri pe verticală).
;; Funcția trebuie implementată fără a folosi recursivitate explicit,
;; ci utilizând doar funcționale.

(define (matrix-to-image matrix)


(above (( (uncurry->curry beside) (((uncurry->curry beside) ( overlay-> empty-image (list (caar
matrix)) ) )
(overlay-> empty-image (list (cadar matrix)) ) ))
(overlay-> empty-image (list (caddr(car matrix)))) )
(( (uncurry->curry beside) (((uncurry->curry beside) ( overlay-> empty-image (list (caadr
matrix)) ) )
(overlay-> empty-image (list (cadar(cdr matrix)) ) ) ))
(overlay-> empty-image (list (car (cddar(cdr matrix))) )) )
(( (uncurry->curry beside) (((uncurry->curry beside) ( overlay-> empty-image (list (car (caddr
matrix))) ) )
(overlay-> empty-image (list (cadr (caddr matrix))))))
( overlay-> empty-image (list (caddr (caddr matrix))) ) )
))

(check-exp (matrix-to-image MATRIX) .)

(exercițiul 6 : 1 punct)
;; Implementați funcția mirror care obține matricea inițială dată ca parametru vazută în oglindă.
;; Funcția trebuie implementată fără a folosi recursivitate explicit,
;; ci utilizând doar funcționale.

(define (mirror matrix)


(cons (reverse (car matrix)) ( cons (reverse (cadr matrix)) ( cons (reverse (car (cddr matrix)))
'() ) ) ))
(exercițiul 7 : 1.5 puncte)
;; Implementați funcția transpose care obține transpusa matricei dată
;; ca parametru.
;; Funcția trebuie implementată folosind cel puțin o funcțională.

(define (transpose M)
(if (null? (car M))
'()
(cons (map car M)
(transpose (map cdr M)))))

(exercițiul 8 : 1.5 puncte)


;; Implementați funcția slim-vertical care primește ca parametru o
;; matrice de imagini și întoarce o listă în care fiecare element reprezintă
;; imaginile suprapuse (jos -> sus) de pe o coloană.
;; Funcția trebuie implementată fără a folosi recursivitate explicit,
;; ci utilizând doar funcționale.

(define (slim-vertical matrix)


(slim-horizontal (mirror (transpose matrix))))

(exercițiul 9 : 2 puncte BONUS)


;; Pornind de la o listă de imagini L, se dorește:
;; (a) păstrarea doar a celor a căror înălțime este mai mare decât min-height;
;; - pentru determinarea înălțimii, folosiți funcția (image-height img) care primește
;; ca parametru o imagine img și întoarce înălțimea acesteia, ca integer
;; (b) construirea unei matrice, ale cărei linii sunt identice cu lista rezultată
;; în urma punctului (a), dar în care fiecare imagine de pe diagonala principală
;; este acum rotită cu 30 de grade înmulțit cu numărul liniei pe care se află (numerotarea
;; liniilor va începe de la 1).
;; ATENȚIE! Utilizați funcționale în implementare! NU este permisă simularea accesului
;; aleator în listă (take, drop, sau alte funcții definite de voi cu comportament similar).
;; Rotirea unei imagini se poate face cu ajutorul funcției (rotate angle image) care primește ca
;; argumente un unghi (angle) și o imagine (image), întorcând astfel imaginea inițială, rotită cu
;; angle grade, în sens trigonometric.
;; Exercițiul nu trebuie rezolvat exclusiv cu funcționale, ci se poate combina cu recursivitate.

(define (greater L min-height)


(filter (lambda (x) (> (image-height x) min-height)) L)
)

(define (get-rotated L line)


(foldl (lambda (x) (rotate (* line 30) x)) L))

;;(define concatM (L1 L2 L3)


;;(above L1 (above L2 L3)))

(define (rotate-diag L min-height)


'(apply map (list L))
)

(exercițiul 10 : 3 puncte BONUS)


;; Dându-se o listă de imagini, care poate conține duplicate, se cere implementarea
;; funcției scale&overlay, care urmărește:
;; (a) eliminarea duplicatelor din lista inițială L (rezultă obținerea unei liste L')
;; - se va păstra cel mai din STÂNGA element dintre duplicate
;; (b) scalarea fiecarui element din L' cu un factor f(elem) egal cu times/n, cu n egal cu valoarea
;; numărului de apariții al elementului în lista originală (rezultă obținerea unei liste L'') și times
;; parametru dat funcției
;; - f(elem) = times/occurences(elem, L)
;; - nu aveți voie să folosiți funcția count pentru numărul de apariții
;; - se va folosi funcția scale care primește un factor pentru scalare
;; și o imagine, și întoarce imaginea scalată (mărită)
;; (c) sortarea elementelor din lista L'' după înălțimea imaginilor
;; (d) suprapunerea imaginilor din L'', astfel:
;; - cea mai din spate imagine este aceea cu înălțimea cea mai mare din L''
;; - următoarea imagine este aceea cu înălțimea cea mai mare din L''\{imaginea anterioară}
;; - ș.a.m.d până când imaginea cea mai din față este imaginea cu cea mai mică înălțime din L''
;; Se garantează că în L'' nu vor exista două imagini cu aceeași înălțime

(define (scaler L times)


(foldl (lambda (x y) (cons x (map (lambda (z x) (scale (/ times (count z x)))) x (list L)) y)) L L))
;;(map (lambda (x) (scale (/ times (count x images) x )) initial images)

;;)

(define (merge-lists xs ys)


(cond
[(null? xs) ys]

[(null? ys) xs]

[(< (image-height (car xs)) (image-height (car ys)))

(cons (car xs) (merge-lists (cdr xs) ys))]

[
(cons (car ys) (merge-lists xs (cdr ys)))]))

(define (mergesort L)
(cond
[(or (null? L) (null? (cdr L))) L]
[(null? (cddr L))
(merge-lists (list (car L)) (cdr L))]
[#t
(let ([x (ceiling (/ (length L) 2))])
(merge-lists (mergesort (take L x))
(mergesort (drop L x))))]))

(define (scale&overlay L times)

(overlay<- (mergesort (remove-duplicates-left (scaler L times))))


)

LAB 4:
(exercițiul 1 : 1 puncte)
;; Funcția compute-square-area primește ca argument o funcție
;; ce returnează un număr, reprezentând latura unui pătrat;
;; compute-square-area trebuie să calculeze aria acelui pătrat.
;; Atenție: Aveți voie să aplicați get-length o singură dată.
;; Nu puteți folosi `exp`/`expt`
(define (compute-square-area get-length)
(let ((l (get-length)))
(* l l )))
(exercițiul 2 : 1 puncte)
;; Funcția compute-length primește ca argumente 3 funcții:
;; - get-line-segment => întoarce un segment de dreaptă.
;; - get-start-point => primește un segment de dreaptă și
;; întoarce punctul din care segmentul începe.
;; - get-end-point => primește un segment de dreaptă și
;; întoarce punctul în care segmentul se termină.
;;
;; Un punct este reprezentat printr-o pereche de numere.
;; Un segment de dreaptă este reprezentat printr-o pereche de puncte.
;;
;; compute-length trebuie să calculeze lungimea segmentului de dreaptă.
;; (distanța dintre punctul de început și punctul de sfârșit)
;; Atenție: Fiecare funcție primită drept argument trebuie aplicată o singură dată.
(define (compute-length get-line-segment get-start-point get-end-point)
(let* ((segm (get-line-segment))
(start (get-start-point segm))
(end (get-end-point segm))
(start-x (car start))
(start-y (cdr start))
(end-x (car end))
(end-y (cdr end)))
(sqrt (+ (* (- end-x start-x) (- end-x start-x)) (* (- end-y start-y) (- end-y start-y))))))

(exercițiul 3 : 1 puncte)
;; Definiți funcția distance care calculează distanța
;; dintre două puncte bazându-se pe funcția compute-length.
;; Identificați închiderea funcțională și arătați-o
;; asistentului pentru a vă puncta acest exercițiu.
(define (distance p1 p2)
(let* ((segm (cons p1 p2))
(get-segm (λ () segm))
(get-start (λ (segm) p1))
(get-end (λ (segm) p2)))
(compute-length get-segm get-start get-end)))

(exercițiul 4 : 1 puncte)
;; Fie f(x) o funcție oarecare,
;; Calculați valorile funcției f(x), a < x <= b cu pasul step.
;; Atenție: Folosiți named let.
;; Nu apelați recursiv `compute-f-with-step`.
;; Nu aveți voie să folosiți funcționale.
(define (compute-f-with-step f a b step)
(let calculate ((x a))
(if (> x b)
null
(cons (f x) (calculate (+ x step))))))

(exercițiul 5 : 1 puncte)
;; Funcția num-concat primește două numere și le concatenează.
;; ex:
;; > (num-concat 33 22)
;; 3322
;; Suprascrieți procedura `+` doar în contextul local pentru
;; a realiza concatenarea dintre două numere.
;; Hint: `string-append` concatenează două string-uri
;; Hint: Puteți folosi funcțiile `number->string` și `string->number`
(define (num-concat x y)
(let ((+ (λ (x y) (string->number (string-append (number->string x) (number->string y))))))
(+ x y)));; Nu stergeți această linie.

(exercițiul 6 : 3 puncte)
;; Definiți funcția compute-perimeter care primește un poligon reprezentat
;; printr-o listă de puncte și calculează perimetrul acestuia.
;; Atenție: Nu aveți voie să definiți funcții ajutătoare în exteriorul funcției compute-perimeter.
;; Nu aveți voie să folosiți funcționale.
;; Hint: Folosiți-vă de funcția distance
(define (compute-perimeter points)
(let perim ((current-points points))
(if (null? (cdr current-points))
(distance (car points) (car current-points))
(+ (distance (car current-points) (second current-points))
(perim (cdr current-points))))))

(exercițiul 7 : 2 puncte)
;; Se dau 3 secvențe separate printr-un separator.
;; Definiți funcția `3-sequence-max` care găsește
;; suma secvenței de sumă maximă.
;; ex:
;; (1 2 3 0 3 5 4 0 5 200) => secvența de sumă maximă este 205
;; Atenție: Nu aveți voie să folosiți fold/apply.
;; Folosiți let-values/let*-values.
;; Hint:: Uitați-vă peste splitf-at.

(define (sum-list L)
(apply + L))

(define (3-sequence-max numbers separator)


(let*-values (((split) (λ (x) (not (= x separator))))
((secv1 rest1) (splitf-at numbers split))
((secv2 rest2) (splitf-at (cdr rest1) split))
((secv3 rest3) (splitf-at (cdr rest2) split)))
(max (max (sum-list secv1) (sum-list secv2)) (sum-list secv3))))

(exercițiul 8 : 2 puncte BONUS)


;; Redefiniți funcția num-concat pentru a funcționa pe oricâte numere.
;; Atenție: Nu puteți folosi `num-concat`
;; Folosiți funcționale.
;; Înțelegeți cum vă poate ajuta programarea funcțională?
;; Cum ar arăta o suprascriere echivalentă într-un limbaj procedural?
;; Care implementare este mai straightforward, este mai ușor de înțeles
;; și lasă loc pentru mai puține erori?
(define (list-num-concat numbers)
(/ (foldr (λ (x y) (string->number (string-append (number->string x) (number->string y))))
0
numbers) 10))

;; Definiți funcția care gasește toate sufixele posibile pentru un număr.


;; ex:
;; (find-all-suffixes 123) => '(123 23 3)
;; Atenție: Folosiți named let.
;; Există deja definită in laborator funcția number->list.
(define (find-all-suffixes number)
(let add-suffix ((num-list (number->list number)))
(if (null? num-list)
null
(cons (list-num-concat num-list) (add-suffix (cdr num-list))))))

(exercițiul 9 : 1 puncte BONUS)


;; Automatele finite sunt un formalism matematic util pentru a descrie
;; în mod abstract (matematic) procese, sunt folosite des în computer science
;; și le veți întâlni (le-ați întâlnit) la CN, LFA și alte materii.
;; În acest exercițiu vom reprezenta un automat finit prin 3 elemente:
;; initial-state => o stare inițială din care automatul pornește
;; final-state => o stare finală în care automatul se oprește
;; next => o funcție care primește o stare și decide care e următoarea stare.
;; Determinați ordinea de parcurgere a stărilor în urma executării automatului.
;; O execuție a automatului începe din starea inițială initial-state, parcurge
;; stările conform funcției de tranziție next și se încheie în starea finală,
;; ordinea de parcurgere fiind reprezentată sub forma unei liste de stări.
;; Atenție: Trebuie să folosiți named let.
(define (run initial-state final-state next)
(let change-state ((current-state initial-state))
(if (equal? current-state final-state)
(list current-state)
(cons current-state (change-state (next current-state))))))

(exercițiul 10 : 2 puncte BONUS)


;; Folosindu-vă de exerciţiile anterioare generați numărul de lungime k
;; pentru care orice cifră de pe poziția i se poate obține
;; folosind formula i*k+x.
;; Atenție: Folosiți funcția run.
(define (generate-number k x)
(list-num-concat (run x (+ x (* k (sub1 k))) (λ (x) (+ x k)))))

LAB 5:

;; Funcții și valori predefinite pe stream-uri:


;; * stream-cons
;; * stream-first
;; * stream-rest
;; * empty-stream
;; * stream-empty?
;; * stream-map, stream-filter

;; Funcții utile:
;; primește un flux și obține o listă cu primele n elemente din flux
(define (stream-take s n)
(cond ((zero? n) '())
((stream-empty? s) '())
(else (cons (stream-first s)
(stream-take (stream-rest s) (- n 1))))))
;; fluxul numerelor naturale
(define naturals
(let loop ((seed 0))
(stream-cons seed (loop (add1 seed)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(exercițiul 1 : 0.5 puncte)


;; funcția list->stream, care creează un flux din elementele unei liste
(define (list->stream L)
(if (null? L)
empty-stream
(stream-cons (car L) (list->stream (cdr L)))))

(exercițiul 2 : 0.5 puncte)


;; funcția stream-zip-with, care primește o funcție binară și 2 fluxuri și funcționează
;; ca un (map f s1 s2) (stream-map nu merge decât cu funcții unare, nu ca map pe liste)
;; ex: (stream-zip-with * naturals naturals) => stream-ul pătratelor numerelor naturale
(define (stream-zip-with f s1 s2)
(if (or (stream-empty? s1) (stream-empty? s2))
empty-stream
(stream-cons (f (stream-first s1) (stream-first s2))
(stream-zip-with f (stream-rest s1) (stream-rest s2)))))

(exercițiul 3 : 0.5 puncte)


;; fluxul care alternează la infinit elementele a și b: a, b, a, b, ...
(define (repeat a b)
(stream-cons a (repeat b a)))
(exercițiul 4 : 2 puncte)
;; fluxul cu elemente din seria Leibniz: 1, -1/3, 1/5, -1/7, ...
;; Se cere implementarea acestuia prin două metode:
;; - construind fluxul explicit, prin generarea fiecărui element
;; - construind fluxul implicit, prin prelucrarea (fără recursivitate explicită) altui/altor flux/fluxuri
(cum ar fi naturals)
(define leibniz-series-1 ;; explicit
(let loop ((n 1) (prev 1))
(stream-cons (/ prev n) (loop (+ n 2) (- prev)))))

(define leibniz-series-2 ;; implicit


(stream-zip-with / (repeat 1 -1) (stream-filter odd? naturals)))

(exercițiul 5 : 1.5 puncte)


;; fluxul x, y = (f1 x), z = (f2 y), t = (f1 z), u = (f2 t) .... (se dau x, f1 și f2)
;; Se cere o definiție implicită a fluxului (fără a genera recursiv element cu element).
;; Hint: funcția apply
(define (play x f1 f2)
(stream-cons x (stream-zip-with apply
(repeat f1 f2)
(stream-map list (play x f1 f2)))))

(exercițiul 6 : 2 puncte)
;; Folosiți funcția de mai sus pentru a afla cine câștigă jocul următor:
;; - 2 jucători, 1 și 2, pleacă de la un număr natural n > 0
;; - cei 2 alternează mutări, 1 mută primul
;; - o mutare a lui 1 constă în a scădea 1 din n-ul curent, dacă n e multiplu de 3, sau a scădea 3, altfel
;; - o mutare a lui 2 constă în a scădea 2 din n-ul curent, dacă n e multiplu de 4, sau a scădea 1, altfel
;; - câștigă cel care ajunge primul la n <= 0
;; NU ne interesează o soluție matematică, ci una care folosește funcția de mai sus!
(define (winner n)
(car
(stream-first
(stream-filter
(lambda (pair) (<= (cdr pair) 0))
(stream-zip-with cons
(repeat 2 1)
(play n
(lambda (n) (if (zero? (modulo n 3)) (- n 1) (- n 3)))
(lambda (n) (if (zero? (modulo n 4)) (- n 2) (- n 1)))))))))

(exercițiul 7 : 3 puncte)
;; fluxul Bell: (1), (1 2), (2 3 5), (5 7 10 15) ...
;; regula după care se generează listele din flux
;; - prima listă este (1)
;; - următoarea începe cu ultimul element din lista anterioară, apoi
;; elementul cu indice i din lista curentă este suma elementelor cu indice i-1
;; din listele curentă și anterioară (https://en.wikipedia.org/wiki/Bell_triangle)
(define (next-bell L len)
(letrec ((S1 (list->stream L))
(S2 (stream-cons (last L) (stream-zip-with + S1 S2))))
(stream-take S2 len)))

(define bell-stream
(let loop ((L '(1)) (len 2))
(stream-cons L (loop (next-bell L len) (add1 len)))))

;; BONUS
(exercițiul 8 : 1 puncte)
;; funcția curry stream-merge care interclasează 2 fluxuri sortate
;; după următoarea regulă: e1 precede pe e2 dacă (comp (f e1) (f e2))
;; ex: (stream-take ((stream-merge < sqr) naturals naturals) 10) => (0 0 1 1 2 2 3 3 4 4)
(define (stream-merge comp f)
(lambda (s1 s2)
(cond
((stream-empty? s1) s2)
((stream-empty? s2) s1)
((comp (f (stream-first s1)) (f (stream-first s2)))
(stream-cons (stream-first s1) ((stream-merge comp f) (stream-rest s1) s2)))
(else (stream-cons (stream-first s2) ((stream-merge comp f) s1 (stream-rest s2)))))))

(exercițiul 9 : 3 puncte)
;; fluxul perechilor de numere naturale sortate crescător după suma elementelor din pereche
;; rezultatul conține doar perechi (x y) cu x <= y
;; perechile cu aceeași sumă sunt sortate după primul element din pereche
;; Hint: prelucrați 2 fluxuri s1 și s2 unde inițial s1 = s2 = naturals
;; Hint: identificați o structură recursivă pe care puteți folosi stream-merge
(define natural-pairs
(let loop ((s1 naturals) (s2 naturals))
(stream-cons
(list (stream-first s1) (stream-first s2))
((stream-merge <= (lambda (p) (apply + p))) (stream-map (lambda (e2) (list (stream-first s1) e2))
(stream-rest s2))
(loop (stream-rest s1) (stream-rest s2))))))

(exercițiul 10 : 1 puncte)
;; fluxul posibilelor aruncări de 2 zaruri, sortate după suma zarurilor
;; rezultatul conține doar perechi (x y) cu x <= y
;; perechile cu aceeași sumă sunt sortate după primul element din pereche
(define dice-rolls
(let ((dice-stream (list->stream '(1 2 3 4 5 6))))
(let loop ((s1 dice-stream) (s2 dice-stream))
(if (stream-empty? s1)
empty-stream
(stream-cons
(list (stream-first s1) (stream-first s2))
((stream-merge <= (lambda (p) (apply + p))) (stream-map (lambda (e2) (list (stream-first s1)
e2)) (stream-rest s2))
(loop (stream-rest s1) (stream-rest s2))))))))

LAB 6:

1. (1p)
Implementați funcția `unzip2`
-}
unzip2 :: [(stack, b)] -> ([stack], [b])
unzip2 [] = ([], [])
unzip2 ((stack, b) : xs) = (stack : (fst (unzip2 xs)), b : (snd (unzip2 xs)))

{-
2. (1p)
Implementați, folosind obligatoriu list-comprehensions, lista tuturor numerelor prime până la n.
-}
intSqrt = floor . sqrt . fromIntegral

primes :: Int -> [Int]


primes 1 = []
primes n = 2 : [x | x <- [3, 5 .. n], and [mod x p /= 0 | p <- primes (intSqrt x)]]

3. (3p)
Implementați, folosind obligatoriu list-comprehensions, operații pe mulțimi:
intersecție, diferență, produs cartezian. Utilizați ulterior funcțiile definite anterior
pentru stack reprezenta reuniunea mulțimilor.
-}
setIntersection :: Eq stack => [stack] -> [stack] -> [stack]
setIntersection stack b = [x | x <- stack, elem x b]

setDiff :: Eq stack => [stack] -> [stack] -> [stack]


setDiff stack b = [x | x <- stack, not (elem x b)]

cartProduct :: [stack] -> [b] -> [(stack, b)]


cartProduct stack b = [(x, y) | x <- stack, y <- b]

setUnion :: Eq stack => [stack] -> [stack] -> [stack]


setUnion stack b = setDiff stack b ++ b

4. (1.5p)
Implementați o funcție ce grupează elementele egale ale unei liste în liste separate.
Funcția ar trebui să aibă același comportament cu Data.List.group:
http://zvon.org/other/haskell/Outputlist/group_f.html
-}
group2 :: Eq stack => [stack] -> [[stack]]
group2 [] = []
group2 (x : xs) =
let (equal, rest) = span (== x) xs
in (x : equal) : group2 rest

{-
5. (1.5p)
Găsiţi numărul de apariţii ale fiecărui element dintr-o listă în lista respectivă.
Rezultatul va fi returnat ca o listă de tupluri, în care primul element al perechii
va fi elementul din listă, iar al doilea element al perechii va fi numărul de apariţii în listă.
Cum rezultatul va fi similar unui dicţionar, chiar dacă un element va apărea de mai multe ori în
listă,
va trebui să fie prezent într-o singură pereche în dicţionar.

Hint: S-ar putea să aveți nevoie de funcții auxiliare. Puteți folosi "group2" de mai sus pentru
stack grupa elementele egale în liste separate şi "sort" pentru stack sorta o listă.
-}
nrOcc :: Ord stack => [stack] -> [(stack, Int)]
nrOcc = map (\ x -> ((head x), (length x))) . group2 . sort

{-
6. (2p)
Duplicaţi toate cuvintele dintr-o propoziţie.
Exemplu: Ce laborator frumos! -> Ce Ce laborator laborator frumos! frumos!

Hint: Ar putea fi utile funcţiile "concat" sau "++" pentru concatenarea cuvintelor, iar "words" si
"unwords" pentru conversia unei propoziții la o listă de cuvinte si invers.
-}
dup :: String -> String
dup = unwords . map (\w -> w ++ " " ++ w) . words

{-
7. (1p Bonus)
Verificați dacă un șir de caractere este isogramă.
Un șir este isogramă daca niciuna din literele sale nu se repetă (dar alte caractere se pot repeta).

Hint: Pe lângă funcțiile de până acum, "isLetter" si "toLower" v-ar putea ajuta în implementare.
Nu uitați că puteți afla semnătura oricărei funcții din interpretor!
-}
isIsogram :: String -> Bool
isIsogram = all (\p -> (snd p) == 1) . nrOcc . map toLower . filter isLetter
{-
8. (2p Bonus)
Determinați dacă un șir format din caracterele (, [, {, ), ], } este corect parantezat.

Hint: Încercați să adaptați implementarea clasică, ce se folosește de o stivă:


https://ocw.cs.pub.ro/courses/sd-ca/2016/articole/tutorial-04-2
-}
arePairedStack :: (String, String) -> Bool
arePairedStack (stack, "")
| stack == "" = True
| otherwise = False

arePairedStack (stack, x : xs)


| stack == "" && (x == '}' || x == ']' || x == ')') = False
| (x == '}' && head stack == '{') ||
(x == ']' && head stack == '[') ||
(x == ')' && head stack == '(') = arePairedStack (tail stack, xs)
| otherwise = arePairedStack (x : stack, xs)

arePaired :: String -> Bool


arePaired xs = arePairedStack ("", xs)

{-
9. (2p Bonus) Scrieți o funcție ce primește la intrare o literă și întoarce o listă sub forma unui
diamant,
formată din șiruri ce conțin literele de la 'A' pană la cea dată, astfel:

+ pentru litera 'C'


[
" A ",
" B B ",
"C C",
" B B ",
" A "
]

+ pentru litera 'E'


[
" A ",
" B B ",
" C C ",
"D D ",
"E E",
"D D ",
" C C ",
" B B ",
" A "
]

Pentru stack obține litera imediat predecesoare altei litere puteți folosi funcția "pred".
Funcția "prettyPrint" este utilă pentru vizualizarea rezultatului în forma de mai sus.

-}
diamond :: Char -> [String]
diamond ch = let downHalf = down ch
in (reverse (tail downHalf)) ++ downHalf

down :: Char -> [String]


down 'A' = ["A"]
down 'B' = ["B B", " A "]
down ch = (ch : ' ' : (tail (init (head prevDown))) ++ ' ' : ch : []) : map (\line -> " " ++ line ++ " ")
prevDown
where prevDown = down (pred ch)

prettyPrint ch = mapM_ print (diamond ch)

LAB 7:

{-
Funcții auxiliare: conversie Char-Word
-}

charToWord :: Char -> Word8


charToWord = fromIntegral . fromEnum

wordToChar :: Word8 -> Char


wordToChar = toEnum . fromIntegral

{-
1. (1p)
Construiți funcția myCycle, care ia ca argument o listă și întoarce lista
repetată la infinit. Ex: myCycle [1,2,3] = [1,2,3,1,2,3,1,2,3,1,2,3,..]
Hint: Puteți defini funcția point-free, folosind funcții din cadrul
modulului Data.List.
http://hackage.haskell.org/package/base-4.6.0.1/docs/Data-List.html
Observaţie: Nu folosiți în implementare funcția cycle. :-)
-}

myCycle :: [a] -> [a]


myCycle = concat . repeat
{-
2. (2p)
Construiţi o progresie aritmetică şi o progresie geometrică pornind de la
primul termen şi raţia în fiecare dintre cazuri.
Ex: arithmetic 1 3 = [1,4,7,..]
geometric 2 3 = [2,6,18,..]
Hint: folosiţi funcţia iterate din cadrul modulului Data.List.
http://hackage.haskell.org/package/base-4.6.0.1/docs/Data-List.html
-}
arithmetic :: Num a => a -> a -> [a]
arithmetic init r = iterate (+ r) init

geometric :: Num a => a -> a -> [a]


geometric init r = iterate (* r) init

{-
3. (2p)
Construiți o funcție care întoarce un șir infinit de numere pseudo-aleatoare,
plecând de la o valoare „seed” întreagă.

Tipul elementelor listei va fi Word8 (numerele vor fi între 0 și 255).


Folosiți fromIntegral pentru a realiza conversii între tipuri numerice întregi.

Folosiți funcțiile definite în modulul System.Random pentru a genera numere.


http://www.haskell.org/ghc/docs/6.12.2/html/libraries/random-1.0.0.2/System-Random.html
Ex: > take 10 $ randoms 42
[38,166,220,81,67,142,213,118,105,10]
Hint: Folosiți-vă de mkStdGen, next și (eventual) alte funcții din
System.Random. *Nu* este necesară folosirea de funcții care întorc
valori de tipul IO.
-}

randoms :: Int -> [Word8]


randoms seed = map (fromIntegral . fst) $ iterate (next . snd) (next $ mkStdGen seed)

{-
4. (1p)
Implementați o funcție care convertește un element de tipul Word8 (0 - 255) într-un caracter
alfabetic ('a' - 'z').

Ex: > map wordToAlpha [0, 1, 2, 3, 26, 27, 28, 29]


"abcdabcd"

Recomandare: folosiți o clauză where sau let.

Hint: Puteți folosi funcțiile 'ord' si 'chr' din Data.Char.


Hint: Puteți folosi funcțiile fromIntegral, mod.
-}
wordToAlpha :: Word8 -> Char
wordToAlpha x = chr $ ord 'a' + index
where
index = fromIntegral $ mod x 26

{-
5. (1p)
Implementați o funcție care generează o secvență infinită de caractere alfabetice
pseudo-aleatoare, plecând de la o valoare "seed" întreagă.
Observație: Implementați funcția point-free.
Hint: Folosiți funcțiile randoms si wordToAlpha de la exercițiile anterioare.
-}

randomAlphaKey :: Int -> String


randomAlphaKey = map wordToAlpha . randoms

{-
6. (3p)
Implementați funcția tableToFunc, care primește o listă de asocieri
(caracter-clar, caracter-criptat) și întoarce o funcție de substituție.
Implementați funcția substCrypt, care primește o listă de asocieri
(caracter-clar, caracter-criptat) și un
șir de caractere și întoarce șirul de caractere criptat pe baza tabelei.
Observație: tableToFunc va fi implementată obligatoriu utilizând o clauză
where/let, cu pattern matching.
Observație: substCrypt va fi implementată obligatoriu point-free (nu va
avea parametri expliciți), folosind funcționale și/sau clauze let/where.
-}

rot13Table = [('a','n'),('b','o'),('c','p'),('d','q'),('e','r'),
('f','s'),('g','t'),('h','u'),('i','v'),('j','w'),
('k','x'),('l','y'),('m','z'),('n','a'),('o','b'),
('p','c'),('q','d'),('r','e'),('s','f'),('t','g'),
('u','h'),('v','i'),('w','j'),('x','k'),('y','l'),
('z','m')]

tableToFunc :: [(Char, Char)] -> Char -> Char


tableToFunc table key = crypted
where (_, crypted) : _ = filter (\p -> key == fst p) table
substCrypt :: [(Char, Char)] -> String -> String
substCrypt = map . tableToFunc

{-
7. (Bonus - 1p)

Implementați funcția getRotTable, care produce o listă de asocieri


(caracter-clar, caracter-criptat). Funcția primește un parametru 'offset' și
va construi o asociere între fiecare caracter alfabetic (litere mici) și
caracterul aflat după următoarele 'offset' poziții.

Ex: > getRotTable 1


[('a','b'),('b','c'),('c','d'),('d','e'),('e','f'),('f','g'),('g','h'),
('h','i'),('i','j'),('j','k'),('k','l'),('l','m'),('m','n'),('n','o'),
('o','p'),('p','q'),('q','r'),('r','s'),('s','t'),('t','u'),('u','v'),
('v','w'),('w','x'),('x','y'),('y','z'),('z','a')]

Ex: > getRotTable 13


[('a','n'),('b','o'),('c','p'),('d','q'),('e','r'),('f','s'),('g','t'),
('h','u'),('i','v'),('j','w'),('k','x'),('l','y'),('m','z'),('n','a'),
('o','b'),('p','c'),('q','d'),('r','e'),('s','f'),('t','g'),('u','h'),
('v','i'),('w','j'),('x','k'),('y','l'),('z','m')]

Hint: Puteți să folosiți zip sau zipWith


http://hackage.haskell.org/package/base-4.11.0.0/docs/Prelude.html#g:20

Hint: Pentru a genera lista cu toate caracterele din alfabet,


puteți folosi expresia: ['a'..'z']
-}
genRotTable :: Int -> [(Char, Char)]
genRotTable offset = zip initialChars newChars
where
initialChars = ['a'..'z']
newChars = map (\c -> chr $ 97 + mod (offset + ord c - 97) 26) initialChars

{- 8. (Bonus - 1p)
Ne propunem să implementăm o funcție de criptare numită encryptVigenere.
Mai multe despre aceasta tehnică de criptare:
http://practicalcryptography.com/ciphers/classical-era/vigenere-gronsfeld-and-autokey/

Funcția encryptVigenere primește următoarele argumente: șirul de caractere alfabetice


care trebuie criptat (plaintext) și o cheie secreta (secret-key).

Fie `xi`, indexul caracterului alfabetic de pe pozitia 'i' din sirul de caractere original.
Fie `yi`, indexul caracterului alfabetic de pe pozitia 'i' din cheia secreta generată la pasul anterior.

Caracterul criptat de pe pozitia 'i' va putea fi obținut alegând cel de-al `xi` caracter
din alfabet, dacă alfabetul ar fi rotit cu `yi` poziții. (vom considera alfabetul indexat de la 0)

Ex:
plaintext: 'def'
secretkey: 'bcd'

Pentru prima poziție din șir:


`xi` = 'd' (caracterul cu indexul 3 din alfabet)
`yi` = 'b' (caracterul cu indexul 1 din alfabet)
Alfabetul rotit cu 1 pozitie: "bcdefghijklmnopqrstuvwxyza"
Prin urmare, primul caracter criptat va fi: 'e' (al 3-lea element din alfabetul rotit)
Daca cheia are o lungime mai mică decat șirul care trebuie criptat,
va fi duplicată pană va ajunge la aceeași dimensiune.

> encryptVigenere "attackatdawn" "lemon"


"lxfopvefrnhr"
-}

encryptVigenere :: String -> String -> String


encryptVigenere clearText key = zipWith tableToFunc rotTables clearText
where
rotTables = cycle $ map (\c -> genRotTable $ ord c - 97) key

{- 9, (Bonus - 2p)
Funcția xorNumbers face xor între două numere întregi.

Exemplu:
xorNumbers 10 3 = 9

Explicație:
Numar -> Reprezentare Binară
10 -> 0 0 0 0 1 0 1 0
3 -> 0 0 0 0 0 0 1 1
--------------------------(xor)
9 -> 0 0 0 0 1 0 0 1

Funcția xorStrings face xor între două șiruri de caractere.


Pentru fiecare poziție i, xorStrings face xor între caracterele aflate
pe poziția i în cele 2 șiruri
În cazul în care lungimile sunt diferite, șirul mai scurt se extinde
(prin concatenare cu el însuși) până la lungimea celuilalt șir.
Exemplu:
xorStrings "zz" "aa" = "\ESC\ESC"

Explicație:
Șir -> Valoare
"zz" -> '0x7a' '0x7a'
"aa" -> '0x61' '0x61'
------------------------------(xor)
"\ESC\ESC" -> '0x1b' '0x1b'

Șir -> Valoare


"zz" -> 122 122
"aa" -> 97 97
------------------------------(xor)
"\ESC\ESC" -> 27 27
-}

xorNumbers :: Int -> Int -> Int


xorNumbers = xor

xorStrings :: [Char] -> [Char] -> [Char]


xorStrings a b = map chr $ zipWith xor (map ord long) (map ord expadedShort)
where
short
| length a > length b = b
| otherwise =a
long
| length a <= length b = b
| otherwise =a
expadedShort = take (length long) (cycle short)

{- 10, (Bonus - 1p)


Funcția rollingXor va implementa același comportament ca xorStrings
singura diferență fiind că șirurile pot avea lungimi diferite.

Exemplu:
rollingXor "zb" "aaa" = "\ESC\CAN\ESC"

Explicație:
"zb" se extinde la "zbz" si se face xorStrings între "zbz" si "aaa".
-}

rollingXor :: [Char] -> [Char] -> [Char]


rollingXor = xorStrings

LAB 8:
{-
PP, laboratorul 8: tipuri de date utilizator
-}
{-
1. (2p) Vectori
Se dă tipul de date Vector, reprezentând vectori din spațiul R^3.

Implementați următoarele operații cu vectori:


- norma unui vector
- normalizarea unui vector
- produsul scalar (dot product) dintre doi vectori
- produsul vectorial (cross product) dintre doi vectori
- adunarea a doi vectori
- scăderea a doi vectori
- verificarea ortogonalității unor vectori

Explicații

Fie a și b doi vectori din R^3 considerați de forma:


a = a1 * i + a2 * j + a3 * k
b = b1 * i + b2 * j + b3 * k
Norma vectorului a este egală cu:
|a| = sqrt(a1^2 + a2^2 + a3^2)
Normalizarea vectorului a este egală cu:
a' = a / |a| = (a1 / |a|) * i + (a2 / |a|) * j + (a3 / |a|) * k
Produsul vectorial al celor doi vectori o să fie egal cu:
a x b = (a2 * b3 - a3 * b2) * i + (a3 * b1 - a1 * b3) * j + (a1 * b2 - a2 * b1) * k
Produsul scalar al celor doi vectori o să fie egal cu:
a • b = a1 * b1 + a2 * b2 + a3 * b3
Produsul scalar a doi vectori u și v este 0 dacă și numai dacă u și v sunt ortogonali.

Pentru mai multe detalii, consultați:


https://gerardnico.com/linear_algebra/vector_vector
-}
data Vector = V
{ vx :: Double
, vy :: Double
, vz :: Double
} deriving (Show, Eq)

lengthV :: Vector -> Double


lengthV (V vx vy vz) = sqrt(vx * vx + vy * vy + vz * vz)

normalizeV :: Vector -> Vector


normalizeV (V vx vy vz) = (V (vx / norm) (vy / norm) (vz / norm))
where
norm = lengthV (V vx vy vz)

dotV :: Vector -> Vector -> Double


dotV (V vx vy vz) (V va vb vc) = vx * va + vy * vb + vz * vc

crossV :: Vector -> Vector -> Vector


crossV (V vx vy vz) (V va vb vc) = (V (vy * vc - vz * vb) (vz * va - vx * vc) (vx * vb - vy * va))

addV :: Vector -> Vector -> Vector


addV (V vx vy vz) (V va vb vc) = (V (vx + va) (vy + vb) (vz + vc))

subV :: Vector -> Vector -> Vector


subV (V vx vy vz) (V va vb vc) = (V (vx - va) (vy - vb) (vz - vc))

orthogonalV :: [Vector] -> Bool


orthogonalV (v : []) = True
orthogonalV (v : vs)
| all (== 0) (map (dotV v) vs) == True = orthogonalV vs
| otherwise = False

2. (4p) Liste imbricate


Definiți un tip de date SList a care să aibă funcționalități
asemănătoare listelor din limbajele Lisp (e.g. Scheme, Racket, Clojure),
permițând componente la diferite niveluri de imbricare.
Ex: Lista din Racket '(1 (3 4) (2)) să poată fi definită în Haskell
folosind SList.
Adițional, definiți:
- emptySList, lista vidă
- consElem, adaugă un element în capul unei liste
Ex: consElem 1 '((3 4) (2)) == '(1 (3 4) (2))
- consList, adaugă o listă (imbricată) în capul unei liste
Ex: consList '(2 3) '(1 2) == '((2 3) 1 2)
- headSList, ia primul element dintr-un SList
- tailSList, ia restul SList-ului
- deepEqual, o funcție ce verifică egalitatea a două SList
- flatten, întoarce lista cu elementele din SList (pe același nivel)
Notare:
(2p) constructorii (emptySList, consElem și consList) și deepEqual
(1p) headSList și tailSList
(1p) flatten
-}
data SList a = UndefinedSList | Elem a | List [SList a] deriving Show

emptySList :: SList a
emptySList = List []

consElem :: a -> SList a -> SList a


consElem x1 (Elem x2) = List [Elem x1, Elem x2]
consElem x (List xs) = List $ Elem x : xs

consList :: SList a -> SList a -> SList a


consList xs (Elem x) = List [xs, Elem x]
consList xs (List ys) = List $ xs : ys

headSList :: SList a -> SList a


headSList (List xs) = head xs

tailSList :: SList a -> SList a


tailSList (List xs) = List $ tail xs
deepEqual :: Eq a => SList a -> SList a -> Bool
deepEqual (Elem x) (Elem y) = x == y
deepEqual (List xs) (List ys)
| length xs == length ys = and $ zipWith deepEqual xs ys
| otherwise = False

flatten :: SList a -> [a]


flatten (Elem x) = [x]
flatten (List xs) = concatMap flatten xs

{-
3. (4p) Arbori binari de căutare
Definiți un tip de date BST a pentru a implementa un arbore binar de
căutare. De asemenea, definiți funcții pentru a insera o valoare într-un
arbore binar de căutare, căutarea unui element într-un arbore binar de
căutare dat, o funcție care întoarce lista elementelor din parcurgerea
în inordine a arborelui și o funcție care întoarce cel mai mic subarbore
care conține două noduri ale căror chei sunt date.
-}
data BST a = UndefinedNode | BSTNode a (BST a) (BST a) | BSTNil deriving Show

insertElem :: (Ord a, Eq a) => BST a -> a -> BST a


insertElem BSTNil x = BSTNode x BSTNil BSTNil
insertElem (BSTNode crtNode leftNode rightNode) x
| x <= crtNode = BSTNode crtNode (insertElem leftNode x) rightNode
| otherwise = BSTNode crtNode leftNode (insertElem rightNode x)

findElem :: (Ord a, Eq a) => BST a -> a -> Maybe a


findElem BSTNil x = Nothing
findElem (BSTNode crtNode leftNode rightNode) x
| x == crtNode = Just x
| x <= crtNode = findElem leftNode x
| otherwise = findElem rightNode x

subTree :: (Ord a, Eq a) => BST a -> a -> a -> Maybe (BST a)


subTree (BSTNode crtNode leftNode rightNode) x y
| foundX == Nothing || foundY == Nothing = Nothing
| x <= crtNode && y <= crtNode = subTree leftNode x y
| x > crtNode && y > crtNode = subTree rightNode x y
| otherwise = Just (BSTNode crtNode leftNode rightNode)
where
foundX = findElem (BSTNode crtNode leftNode rightNode) x
foundY = findElem (BSTNode crtNode leftNode rightNode) y

inorder :: BST a -> [a]


inorder BSTNil = []
inorder (BSTNode crtNode leftNode rightNode) = inorder leftNode ++ [crtNode] ++ inorder
rightNode

{-
4. (BONUS, 2p) Arbore multicăi nevid
Având dat tipul Tree a, definiți funcționala analoagă lui map, care să
aplice o funcție asupra cheilor nodurilor din arbore, și o funcțională
analoagă lui foldl care să parcurgă nodurile în ordinea: rădăcină, copil_1,
copil_2, ... copil_n.
-}

data Tree a = Node


{ value :: a
, children :: [Tree a]
} deriving (Eq, Show)
mapTree :: (a -> b) -> Tree a -> Tree b
mapTree f (Node v c) = (Node (f v) (map (mapTree f) c))

flattenTree :: Tree a -> [a]


flattenTree tree = value tree : foldl (++) [] (map flattenTree $ children tree)

foldlTree :: (b -> a -> b) -> b -> Tree a -> b


foldlTree f acc tree = foldl f acc $ flattenTree tree

{-
5. (BONUS, 3p) Difference lists
Se cere definirea tipului de date "difference list".

Un difference list este o listă "parțial construită", i.e. ale cărei


elemente din coadă nu sunt (neapărat) în întregime cunoscute. De
exemplu, ne putem imagina existența unei liste:

1 : (2 : (3 : xs)) = [1,2,3] ++ xs

unde xs nu are (la momentul construirii) o valoare cunoscută.

În limbajele funcționale putem modela difference lists folosindu-ne de


închideri: putem privi o astfel de structură ca pe o funcție care
așteaptă un parametru (o listă) și întoarce o listă. Exemplul anterior
poate fi astfel exprimat în funcție drept următoarea listă:

(\ xs -> [1,2,3] ++ xs)

Observație: Care este tipul lambda-ului de mai sus?


Avantajul acestei abordări este că permite efectuarea oricărei
operație de adăugare în listă (e.g. concatenarea cu o altă listă) în
O(1), cu dezavantajul că eliminarea este în general mai puțin eficientă,
deoarece presupune evaluarea elementelor structurii.

Se cere, mai concret:


- Definirea ADT-ului difference list (DList), „împăturit peste” o
funcție de tipul [a] -> [a] (e.g. folosind construcția newtype)
- Conversia [a] -> DL a (dlFromList) și invers (dlToList)
- Lista vidă (emptyDL), adăugarea în capul unei liste (consDL) și în
coada ei (snocDL)
- Concatenarea a două liste (appendDL)
- Operații de eliminare: primul element (headDL) și coada (tailDL)
unei liste

Operațiile de lucru cu difference lists (cu excepția celor de


eliminare) vor fi implementate cât mai eficient posibil, i.e. fără a
folosi dlFromList și dlToList.

Pentru mai multe detalii, consultați link-ul:


https://wiki.haskell.org/Difference_list
-}
newtype DList a = DL { listMaker :: [a] -> [a] } -- de rafinat tipul argumentului lui DL

dlFromList :: [a] -> DList a


dlFromList = DL . (++)

dlToList :: DList a -> [a]


dlToList = ($ []) . listMaker

emptyDL :: DList a
emptyDL = DL id

consDL :: a -> DList a -> DList a


consDL x xs = DL ((x :) . listMaker xs)

snocDL :: a -> DList a -> DList a


snocDL x xs = DL (listMaker xs . (x :))

appendDL :: DList a -> DList a -> DList a


appendDL xs ys = DL (listMaker xs . listMaker ys)

headDL :: DList a -> Maybe a


headDL diffList
| null list == True = Nothing
| otherwise = Just $ head list
where
list = dlToList diffList

tailDL :: DList a -> Maybe (DList a)


tailDL diffList
| null list == True || null (tail list) == True = Nothing
| otherwise = Just $ dlFromList $ tail list
where
list = dlToList diffList

LAB 9:

{-
1.(1.5p) Analizați clasa PQueue definită mai jos și scrieți implementările
implicite pentru funcțiile din această clasă:
* fromList
* toList

Clasa PQueue definește interfața pentru toate structurile de coada de priorități


pe care le vom implementa mai jos.
-}

class (Ord a) => PQueue pq a where

-- Construiește o coadă de priorități goală


empty :: pq a

-- Verifică dacă coada este goală


isEmpty :: pq a -> Bool

-- Inserează elem in coada cu priorități


insert :: (Prio, a) -> pq a -> pq a

-- Întoarce primul element din coada de priorități


top :: pq a -> Maybe (Prio, a)

-- Șterge primul element din coada de priorități


-- Dacă coada nu are elemente se va returna coada goală
pop :: pq a -> pq a

-- Creează o coadă de priorități dintr-o lista de tupluri


fromList :: [(Prio, a)] -> pq a
fromList = foldr insert empty

toList :: pq a -> [(Prio, a)]


toList pQueue
| isEmpty pQueue = []
| otherwise = (fromJust $ top pQueue) : (toList $ pop pQueue)

size :: pq a -> Int


size = length . toList -- Pentru BONUS 6

{-
2.(2.0p) Definiți tipul ListPQ care reprezintă o coadă de priorități ca pe o
listă de elemente. Includeți ListPQ în clasa PQueue.
-}

newtype ListPQ a = LPQ [(Prio, a)] deriving Show

instance (Ord a) => PQueue ListPQ a where


empty = LPQ []

insert elem (LPQ pq)


| null pq = LPQ [elem]
| fst elem >= fst (head pq) = LPQ $ elem : pq
| otherwise = LPQ $ head pq : toList (insert elem $ LPQ $ tail pq)

top (LPQ pq)


| null pq = Nothing
| otherwise = Just $ head pq

pop (LPQ pq) = LPQ (tail pq)

isEmpty (LPQ pq) = null pq

{-
LeftistPQ reprezintă o coadă de priorități ca pe un arbore binar.
Fiecare nod va conține elementele:
* Prioritatea
* Rank-ul
* Subarborele stang
* Subarborele drept
Referință - pentru mai multe detalii despre construcție: http://typeocaml.com/2015/03/12/heap-
leftist-tree/
Vizualizare: https://www.cs.usfca.edu/~galles/visualization/LeftistHeap.html
-}

type Rank = Int

data LeftistPQ a = Empty { rank :: Rank } |


Node { rank :: Rank, nodeVal :: (Prio, a), left :: LeftistPQ a, right :: LeftistPQ a }

{-
3.(2.5p) Definiți operația de "merge" care primește 2 parametri de tipul LeftistPQ și intoarce
un nou LeftistPQ obținut prin combinare.
Cazuri de tratat:
* Dacă unul dintre noduri este Empty
* Dacă ambele noduri sunt Empty
* Dacă nodurile nu sunt Empty
Trebuie definită și operația inorder pentru parcurgerea arborelui - este folosit la validare
-}

merge :: LeftistPQ a -> LeftistPQ a -> LeftistPQ a


merge (Empty _) tree = tree
merge tree (Empty _) = tree
merge tree1@(Node r1 (p1, val1) left1 right1) tree2@(Node _ (p2, _) _ _)
| p1 <= p2 = merge tree2 tree1
| rank left1 >= rank mergedTree = (Node (1 + rank mergedTree) (p1, val1) left1 mergedTree)
| otherwise = (Node (1 + rank left1) (p1, val1) mergedTree left1)
where
mergedTree = merge right1 tree2

inorder :: LeftistPQ a -> [(Prio, a)]


inorder (Empty _) = []
inorder (Node r val leftTree rightTree) = inorder leftTree ++ [val] ++ inorder rightTree

{-
4.(1.5p) Includeți LeftistPQ în PQueue
-}

instance (Ord a) => PQueue LeftistPQ a where

empty = Empty 0

isEmpty (Empty _) = True


isEmpty _ = False

insert elem = merge (Node 1 elem empty empty)

top pq
| isEmpty pq = Nothing
| otherwise = Just $ nodeVal pq

pop pq = merge (left pq) (right pq)


{-
5.(1.0p) Definiți funcția convert care face conversia intre cele 2 tipuri de reprezentări
-}

convert :: (PQueue pq1 a, PQueue pq2 a) => pq1 a -> pq2 a


convert = fromList . toList
7.(BONUS 2.0p) Adăugați tipurile ListPQ și LeftistPQ în clasa MyFoldable
Funcția f primește drept parametri: o valoare din coadă (al doilea element din tuplu)
și acumulatorul.
Pentru ListPQ foldr' ar trebui să aibă același comportament ca foldr.
Pentru LeftistPQ foldr' ar trebui să parcurgă arborele dreapta, rădăcină, stânga.

Reminder:
:t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b

În Haskell 8.x.x tipul arată în felul următor


:t foldr
foldr :: (Foldable t) => (a -> b -> b) -> b -> t a -> b

Clasa Foldable caracterizează tipurile care pot fi "reduse" la o anumită valoare utilizând
operații specifice (foldr, foldl).
Foldable t este o constrângere de tip, iar "t a" este un container care conține valori de tipul a.

Mai multe informații: https://wiki.haskell.org/Foldable_and_Traversable


-}

class MyFoldable f where


foldr' :: (a -> b -> b) -> b -> f a -> b

instance MyFoldable ListPQ where


foldr' f acc (LPQ pq) = foldr (\ elem -> f $ snd elem) acc pq

instance MyFoldable LeftistPQ where


foldr' f acc = foldr (\ elem -> f $ snd elem) acc . inorder

{-
8.(BONUS 1.0p) Adăugați tipurile ListPQ și LeftistPQ în clasa MyFunctor
Funcția f primește ca parametru o valoare din coadă (al doilea element din tuplu)
-}

class MyFunctor f where


fmap' :: (Ord a, Ord b) => ((Prio, a) -> (Prio, b)) -> f a -> f b

instance MyFunctor ListPQ where


fmap' f (LPQ pq) = fromList $ map f pq

instance MyFunctor LeftistPQ where


fmap' f = fromList . map f . inorder

{-
9.(BONUS 2.0p) Adăugați LeftistPQ în clasa Show
Va trebui ca arborele să fie afișat în modul următor:
"--" x nivel în arbore {valoare din nod}
Dacă nodul este Empty atunci se va afișa în loc de {valoare din nod} "empty"
Ex: Node _ (3,4) {Node _ (4,5) Empty Empty} {Node _ (5,6) {Node _ (6,7) Empty Empty}
Empty} -- nu ne interesează rankul
--(3,4)
----(4,5)
------empty
------empty
----(5,6)
------(6,7)
--------empty
--------empty
------empty <-- și aici este newline la final

Hint: Parcurgere preordine


-}

showLeftist :: (Show a) => String -> LeftistPQ a -> String


showLeftist dashes (Empty _) = dashes ++ "empty\n"
showLeftist dashes (Node _ val left right) = dashes ++ (show val) ++ "\n" ++ leftShow ++
rightShow
where
leftShow = (showLeftist newDashes left)
rightShow = (showLeftist newDashes right)
newDashes = dashes ++ "--"

instance (Show a) => Show (LeftistPQ a) where


show = showLeftist "--"

LAB 10:

%% Observați diferențele dintre unificare și testarea echivalenței:

% ?- X = Y.
% X = Y.

% ?- X == Y.
% false.

%% Observați diferențele dintre ==/2, =/2, =:=/2 și is/2


%% (echivalență, unificare și evaluari aritmetice).
% ?- X = 1 + 2.
% X = 1+2.

% ?- X == 1 + 2.
% false.

% ?- X is 1 + 2.
% X = 3.

% ?- X =:= 1 + 2.
% ERROR: =:=/2: Arguments are not sufficiently instantiated

%% Predicatele pot fi adevărate sau false, ele nu pot întoarce valori.


%% Rezultatul obținut va fi unul din argumentele predicatului.
%% Pentru claritate, antetele predicatelor se scriu sub forma:
%% modulo(+X,+Y,-Z)
%% pentru a diferenția intrarile (+) de iesiri(-).
%% Acele argumente care pot fi fie intrări, fie ieșiri se prefixează cu '?'.
modulo(X, Y, X) :- X < Y.
modulo(X, Y, Z) :- X >= Y, X1 is X - Y, modulo(X1, Y, Z).

%% Dacă folosim cut, se va incerca satisfacerea celei de-a


%% doua reguli doar atunci cand X<Y este fals, deci nu mai
%% este nevoie să specificăm ca X>=Y, precum în cazul anterior.
%% Predicatul cut ne poate ajuta la optimizarea codul scris
%% de noi, împiedicând resatisfacerea anumitor predicate.
modulo1(X, Y, X) :- X < Y, !.
modulo1(X, Y, Z) :- X1 is X - Y, modulo1(X1 , Y, Z).
%% membru(?Elem,+Lista)
%% Observați ce se intampla daca cereti membru(X,[a,b,c]).
%% Pentru a satisface scopul membru(X,[a,b,c]), se încearcă
%% legarea variabilei X la o valoare. Deoarece procesul nu
%% se oprește până când scopul nu este satisfăcut în toate
%% modurile posibile, X se va lega pe rând la a, b și c.
%% Aceasta este puterea generativă a limbajului.
%% Observați că dacă am fi scris prima regulă sub forma:
%% membru(Elem,[Elem|_]):-!.
%% nu ar mai fi avut același comportament.
membru(Elem, [Elem | _]) :- !.
membru(Elem, [_ | Rest]) :- membru(Elem, Rest).

%% Verifică daca o lista este sortată.


%% e_sortata(+Lista).
e_sortata([]).
e_sortata([_]).
e_sortata([X, Y | Rest]) :- X =< Y, e_sortata([Y | Rest]).

%% Inserarea unui element într-o listă sortată.


%% Observați cum lucrăm asupra rezultatului prin forma pe care i-o dam
%% în antetul regulii.
%% ins(+Elem,+Lista,-Rez)
ins(X, [], [X]).
ins(X, [Y | R], [X, Y | R]) :- X =< Y, !.
ins(X, [Y | R], [Y | R1]) :- ins(X, R, R1).

%% Calculează lungimea unei liste în două moduri.


%% Observați cum putem construi rezultatul la întoarcerea
%% din recursivitate sau intrând în recursivitate, folosind
%% un acumulator, ce inițial va fi 0.
%% lungime(+Lista,-Lungime)
lungime([], 0).
lungime([_ | R], N) :- lungime(R, N1), N is N1 + 1.

%% Observați că atunci când am terminat de parcurs lista,


%% lungimea acesteia este egale cu cea calculată în acumulator.
%% lungime(+Lista,+Acumulator,-Lungime)
lungime2([], N, N).
lungime2([_ | R], N, M) :- N1 is N + 1, lungime2(R, N1, M).

%% Determina al N-lea numar fibonacci.


%% fibonacci(+N,-Fibo)
fibonacci(N, N) :- N =< 1, !.
fibonacci(N, F) :- N1 is N - 1, N2 is N - 2,
fibonacci(N1, F1),
fibonacci(N2, F2),
F is F1 + F2.

%% Șterge un element din lista.


%% Observati cum lucram asupra rezultatului prin forma pe care i-o dam
%% în antetul regulii.
%% Încercați să apelati remove(1,X,[2,3]).
%% remove(+Elem,+Lista,-ListaNoua)
remove(E, [E | R], R).
remove(E, [F | R], [F | L]) :- remove(E, R, L).

%% Determină permutări ale listei.


%% Le dă pe rand, la cerere (nu toate într-o listă).
%% Observați cum ne folosim de remove pentru a genera,
%% la fiecare resatisfacere a sa, o permutare P în care elementul
%% F este inserat pe altă poziție în P1.
%% perm(+Lista,-Permutare)
perm([], []).
perm([F | R], P) :- perm(R, P1), remove(F, P, P1).

%% LABORATOR 10
%% Prolog – Intro

:- discontiguous exercitiul/2.
%% -----------------------------------------------------------------------------
exercitiul(1, [1, punct]).
%% myConcat/3
%% myConcat(?List1, ?List2, ?List)
%% 'List' este lista formată prin concatenarea listelor 'List1' și 'List2'.

myConcat([], L, L).
myConcat([Head | Tail], L, [Head | R]) :- myConcat(Tail, L, R).

%% -----------------------------------------------------------------------------
exercitiul(2, [0.5, puncte]).
%% myReverse/2
%% myReverse(?List, +RevList)
%% 'RevList' este o listă ce conține elementele listei 'List' în ordine inversă.
%% Regulile pot conține și predicatul myConcat/3.

myReverse([], []).
myReverse([Head | Tail], ResList) :- myReverse(Tail, ResTail),
myConcat(ResTail, [Head], ResList).
%% -----------------------------------------------------------------------------
exercitiul(3, [0.5, puncte]).
%% myReverseAcc/2
%% myReverseAcc(?List, ?Acc, ?RevList)
%% 'RevList' este o listă ce conține elementele listei 'List' în ordine inversă
%% și elementele listei 'Acc'.
%% (Indicație: 'Acc' se va comporta precum un acumulator)
%% Regulile nu trebuie să conține alte predicate (în afară de "cut" și ",").

myReverseAcc([], Acc, Acc).


myReverseAcc([Head | Tail], Acc, Res) :- myReverseAcc(Tail, [Head | Acc], Res).

%% -----------------------------------------------------------------------------
exercitiul(4, [1, punct]).
%% extract/4
%% extract(+List, +Start, +End, -Range)
%% 'Range' reprezintă lista formată din elementele listei 'List' aflate
%% pe pozițiile din intervalul 'Start' .. 'End'. 'Start' va fi întotdeauna
%% mai mic decât 'End'. Primul element se află pe poziția 0. Dacă 'End'
%% depășește lungimea listei, 'Range' va conține toate elementele de la 'Start'
%% la finalul listei.

extract([], _, _, []).
extract([Head | _], 0, 0, [Head]).
extract([Head | Tail], 0, End, [Head | Res]) :- End1 is End - 1,
extract(Tail, 0, End1, Res).
extract([_ | Tail], Start, End, Res) :- Start1 is Start - 1, End1 is End - 1,
extract(Tail, Start1, End1, Res).
%% -----------------------------------------------------------------------------
exercitiul(5, [0.5, puncte]).
%% factorial/2
%% factorial(+N, -Fact)
%% 'Fact' este factorialul lui 'N'.
%% N va fi mereu legat la un număr natural.

factorial(N, 1) :- N < 2, !.
factorial(N, Fact) :- N1 is N - 1, factorial(N1, Fact1), Fact is Fact1 * N.

%% -----------------------------------------------------------------------------
exercitiul(6, [0.5, puncte]).
%% factorial2/2
%% factorial2(?N, ?Fact)
%% 'Fact' este factorialul lui 'N'.
%% Cel puțin unul dintre cele două argumente trebuie să fie legat la un număr.

factorial2(0, 1).
factorial2(N, Fact) :- factorial2(N1, Fact1), N is N1 + 1, Fact is Fact1 * N.

%% -----------------------------------------------------------------------------
exercitiul(7,[1, punct]).
%% palindrom/2
%% palindrom(+List)
%% 'List' este un palindrom.

palindrom(L) :- myReverse(L, L).


%% -----------------------------------------------------------------------------
exercitiul(8, [1, punct]).
%% sublista/2
%% sublista(+List, ?SubList)
%% 'SubList' este o sublistă a lui 'List' ('SubList' poate fi obținută prin
%% eliminarea a zero sau mai multe elemente din 'List'

sublista([], []).
sublista([_ | Tail], SubList) :- sublista(Tail, SubList).
sublista([Head | Tail], [Head | SubList]) :- sublista(Tail, SubList).

%% -----------------------------------------------------------------------------
%% -----------------------------------------------------------------------------
%% Se dau următoarele fapte ce descriu arcele unei păduri de arbori binari.
%% Fiecare nod poate avea maximum doi fii.

nod(a). nod(b). nod(c). nod(d). nod(e). nod(f). nod(g).


nod(h). nod(i). nod(j). nod(k). nod(l).
nod(m).
nod(n). nod(p). nod(o).

%% -----------------------------------------------------------------------------
%% arc/2

arc(a,b). arc(a,c). arc(b,d). arc(c,e). arc(c,g). arc(e,f).


arc(h,i). arc(h,j). arc(i,k). arc(j,l).
arc(n,o). arc(o,p).

%% -----------------------------------------------------------------------------
exercitiul(9, [0.5, puncte]).
%% isLeaf/1
%% isLeaf(?Nod)

isLeaf(N) :- nod(N), \+ arc(N, _).

%% -----------------------------------------------------------------------------
exercitiul(10, [0.5, puncte]).
%% isRoot/1

isRoot(N) :- nod(N), \+ arc(_, N).

%% -----------------------------------------------------------------------------
exercitiul(11, [1, punct]).
%% descendantOf/2
%% descendantOf(?X,?Y)
%% Nodul X este un urmaș a lui Y.

descendantOf(N1, N2) :- arc(N2, N1).


descendantOf(N1, N2) :- arc(N3, N1), descendantOf(N3, N2).

%% -----------------------------------------------------------------------------
exercitiul(12, [2, puncte]).
%% descendants/2
%% descendants(?Nod, ?N)
%% Nodul Nod are N urmași.

descendants(Node, 0) :- isLeaf(Node).
descendants(Node, N) :- arc(Node, Node1), \+ (arc(Node, Node2), Node1 \= Node2),
descendants(Node1, N1), N is N1 + 1.
descendants(Node, N) :- arc(Node, Node1), arc(Node, Node2), Node1 \= Node2,
descendants(Node1, N1), descendants(Node2, N2),
N is N1 + N2 + 2.
%%================
%%=====BONUS======
%%================
%% -----------------------------------------------------------------------------
exercitiul(13, [1, punct, bonus]).
%% sameTree/2
%% sameTree(+Nod, +Nod).

sameTree(N, N).
sameTree(N1, N2) :- descendantOf(N1, N2); descendantOf(N2, N1).
sameTree(N1, N2) :- arc(N3, N1), arc(N4, N2), sameTree(N3, N4).

%% -----------------------------------------------------------------------------
exercitiul(14, [2, puncte, bonus]).
%% drum/3
%% drum(?Nod, ?Nod, ?Lista)
%% exemplu: drum(e, b, [e, c, a, b])

drum(N, N, [N]) :- !.
drum(N1, N2, myReverse(C1)) :- descendantOf(N1, N2), drum(N2, N1, C1), !.
drum(N1, N2, [N1 | P]) :- descendantOf(N2, N1), arc(N1, N3),
(descendantOf(N2, N3); N3 == N2), drum(N3, N2, P), !.
drum(N1, N2, [N1 | P]) :- arc(N3, N1), drum(N3, N2, P).

%% -----------------------------------------------------------------------------
exercitiul(15, [2, puncte, bonus]).
%% cost/3
%% cost(+Nod, +Nod, -Cost).
%% un arc in sus costa -1, unul in jos, 1.

cost(N, N, 0) :- !.
cost(N1, N2, C) :- descendantOf(N1, N2), cost(N2, N1, C1), C is C1 * -1, !.
cost(N1, N2, C) :- descendantOf(N2, N1), arc(N1, N3), (descendantOf(N2, N3); N3 == N2),
cost(N3, N2, C1), C is C1 + 1, !.
cost(N1, N2, C) :- arc(N3, N1), cost(N3, N2, C1), C is C1 – 1.

%% ----------------------------------------
%% ----------------------------------------

%% -- BACKTRACKING ATUNCI CÂND CUNOAȘTEM LUNGIMEA SOLUȚIEI --


exercitiul(1, [2, puncte]).
%% Înțelegeți predicatele solve_latin/1, template/1 și correct/1.
%% Observați că lipsește definiția predicatului safe/2.

%% template/1
%% template(?List)
%% List are forma unei soluții pentru problema pătratului latin.
%% Lungimea soluției este cunoscută și fixă.
template([1/1/_, 1/2/_, 1/3/_, 2/1/_, 2/2/_, 2/3/_, 3/1/_, 3/2/_, 3/3/_]).

%% correct/1
%% correct(?Solution)
%% Solution reprezintă o soluție validă pentru problemă.
correct([]):-!.
correct([X/Y/S | Others]):-
correct(Others),
member(S, [a, b, c]),
once(safe(X/Y/S, Others)).
%% Scrieți predicatul safe/2 utilizat în rezolvarea problemei.
%% Predicatul verifică dacă plasarea simbolului S pe coloana X
%% și linia Y este validă în raport cu lista Others. Aceasta
%% are forma [X1/Y1/S1, X2/Y2/S2 ...].

%% TODO
%% safe/2
%% safe(+X/Y/S, +Others)
safe(_, []) :- !.
safe(X/Y/S, [A/B/C | Others]) :- (S \== C; (S == C, X \== A, Y \== B)),
safe(X/Y/S, Others).

%% Întrebați-l pe Prolog "solve_latin(Sol)" pentru a vizualiza


%% soluțiile.

%% -------------------------------------------------------------
%% -------------------------------------------------------------

%% -- BACKTRACKING ATUNCI CÂND NU CUNOAȘTEM LUNGIMEA SOLUȚIEI --


exercitiul(2, [6, puncte]).
%% Înțelegeți cum funcționează predicatele solve/2 și search/3 pentru
%% rezolvarea unei probleme de căutare în spațiul stărilor. Observați
%% utilizarea predicatelor initial_state/2, final_state/2 și
%% next_state/3. fiecare dintre predicate ia ca prim argument problema
%% pe care o rezolvăm.

search(Pb, [CrtState | Other], Sol) :- final_state(Pb, CrtState), !,


reverse([CrtState | Other], Sol).
search(Pb, [CrtState | Other], Sol) :- next_state(Pb, CrtState, NextState),
\+ member(NextState, Other),
search(Pb, [NextState, CrtState | Other], Sol).

solve(Pb, Sol) :- initial_state(Pb, State), search(Pb, [State], Sol).

%% Exemplu: problema țăranului, a lupului, a caprei și a verzei.


%% Vom reprezenta o stare astfel:
%% state(MalTaran, Lista-cu-cine-este-pe-malul-cu-taranul)
%% Vom da acestei probleme numele 'taran'.
%% NOTĂ: implementarea se putea face doar cu 2 clauze pentru next_state

opus(est, vest).
opus(vest, est).

safeTaran([]).
safeTaran([_]).
safeTaran(Cine) :- sort(Cine, [lup, varza]).

allTaran([capra, lup, varza]).

initial_state(taran, state(est, Cine)) :- allTaran(Cine).

final_state(taran, state(vest, Cine)) :- allTaran(All), sort(Cine, All).

%% Țăranul călătorește singur


next_state(taran, state(MalTaran1, Cine1), state(MalTaran2, Cine2)) :-
allTaran(All),
% pe celălalt mal sunt cei care nu sunt pe malul cu țăranul.
setMinus(All, Cine1, Cine2),
% cine rămâne pe vechiul mal este ok.
safeTaran(Cine1),
% țăranul merge pe celălalt mal.
opus(MalTaran1, MalTaran2).

%% Țăranul călătorește cu lupul


next_state(taran, state(MalTaran1, Cine1), state(MalTaran2, Cine2)) :-
allTaran(All),
% lupul este pe același mal cu țăranul, inițial.
member(lup, Cine1),
% pe celălalt mal sunt cei care nu sunt pe malul cu țăranul.
setMinus(All, Cine1, Cine2A),
% pe malul unde ajunge țăranul ajunge și lupul.
Cine2 = [lup | Cine2A],
% cine rămâne pe vechiul mal este ok.
setMinus(All, Cine2, Ramas), safeTaran(Ramas),
% țăranul merge pe celălalt mal.
opus(MalTaran1, MalTaran2).

%% Țăranul călătorește cu varza


next_state(taran, state(MalTaran1, Cine1), state(MalTaran2, Cine2)) :-
allTaran(All),
% varza este pe același mal cu țăranul, inițial.
member(varza, Cine1),
% pe celălalt mal sunt cei care nu sunt pe malul cu țăranul.
setMinus(All, Cine1, Cine2A),
% pe malul unde ajunge țăranul ajunge și varza.
Cine2 = [varza | Cine2A],
% cine rămâne pe vechiul mal este ok.
setMinus(All, Cine2, Ramas), safeTaran(Ramas),
% țăranul merge pe celălalt mal.
opus(MalTaran1, MalTaran2).
%% Țăranul călătorește cu capra
next_state(taran, state(MalTaran1, Cine1), state(MalTaran2, Cine2)) :-
allTaran(All),
% capra este pe același mal cu țăranul, inițial.
member(capra, Cine1),
% pe celălalt mal sunt cei care nu sunt pe malul cu țăranul.
setMinus(All, Cine1, Cine2A),
% pe malul unde ajunge țăranul ajunge și capra.
Cine2 = [capra | Cine2A],
% cine rămâne pe vechiul mal este ok.
setMinus(All, Cine2, Ramas), safeTaran(Ramas),
% țăranul merge pe celălalt mal.
opus(MalTaran1, MalTaran2).

%% vizualizați soluțiile cu
% solve(taran, Sol), validSol(taran, Sol).

% setPlus(+A, +B, -Path)


% concatenează A și B în Path (Atenție! nu elimină duplicate).
setPlus(A, B, Path) :- append(A, B, Path).

% subSet(+Smaller, +Bigger)
% Verifică dacă setul Smaller este inclus în sau egal cu setul Bigger.
subSet(A, B) :- forall(member(X, A), member(X, B)).

% setMinus(+From, +ToRemove, -Path)


% Produce în result lista elementelor din From care nu sunt în ToRemove.
setMinus(From, ToRemove, Path) :-
findall(E, (member(E, From), \+ member(E, ToRemove)), Path).
%% Problema Misionarilor și Canibalilor
%% ====================================
%% Fie un râu cu două maluri, trei misionari, trei canibali și o
%% barcă. Barca și cei 6 se află inițial pe un mal, iar
%% scopul este ca toți să ajungă pe malul opus. Barca are capacitate de
%% maximum două persoane și nu poate călători fără nicio persoană.
%% Găsiți o secvență de traversări, astfel încât nicăieri să nu existe
%% mai mulți canibali decât misionari (pot exista însă pe un mal doar
%% canibali).

%% Scrieți predicatele initial_state, final_state, și next_state


%% pentru problema misionarilor.

%% Pentru o mai bună structură, implementați întâi predicatele boat/2


%% și safeMisionari/2 detaliate mai jos.

% TODO
% boat/2
% boat(-NM, -NC)
% Posibilele combinații de număr de misionari și canibali care pot
% călători cu barca.
%boat(NM, NC) :- bagof(NM, (integer(NM), NM =< 2, NM >= 0), _),
% bagof(NC, ((integer(NC), NC =< NM ; NM =:= 0), NC + NM < 3, NC + NM > 0), _).
%boat(NM, NC) :- bagof(NM/NC, (integer(NM), integer(NC), (NC =< NM ; NM =:= 0), NC + NM
< 3, NC + NM > 0), _).
boat(1, 0).
boat(1, 1).
boat(2, 0).
boat(0, 1).
boat(0, 2).

% TODO
% safe/2
% safe(+NM, +NC)
% Verifică dacă numărul dat de misionari și canibali pot fi pe același
% mal.
% Atenție la de câte ori este adevărat safeMisionari pentru diverse
% valori ale argumentelor - poate influența numărul soluțiilor pentru
% problemă.
safeMisionari(NM, NC) :- once(NM >= NC ; NM == 0).

% TODO
% parseState/3
% parseState(+State, -Mal, -NM_Est, -NC_Est, -NM_Vest, -NC_Vest)
% Întoarce în ultimele 5 argumente malul unde este barca și numerele de
% misionari / canibali de pe malul estic, respectiv vestic, în starea
% dată.
parseState(state(M, NME, NCE, NMV, NCV), M, NME, NCE, NMV, NCV).

% TODO
% initial_state(misionari, -State)
% Determină starea inițială pentru problema misionarilor, în formatul
% ales.
initial_state(misionari, state(est, 3, 3, 0, 0)).

% TODO
% final_state(misionari, +State)
% Verifică dacă starea dată este stare finală pentru problema
% misionarilor.
final_state(misionari, state(vest, 0, 0, 3, 3)).

% TODO
% next_state(misionari, +S1, -S2)
% Produce o stare următoare S2 a stării curente S1.
% Toate soluțiile predicatului next_state pentru o stare S1 dată trebuie
% să fie toate posibilele stări următoare S2 în care se poate ajunge din
% S1.
next_state(misionari, Init, Next):- parseState(Init, M, NME1, NCE1, NMV1, NCV1),
M == est, boat(NM, NC),
NM =< NME1, NC =< NCE1,
NME2 is NME1 - NM, NCE2 is NCE1 - NC,
NMV2 is NMV1 + NM, NCV2 is NCV1 + NC,
safeMisionari(NME2, NCE2),
safeMisionari(NMV2, NCV2),
Next = state(vest, NME2, NCE2, NMV2, NCV2).

next_state(misionari, Init, Next):- parseState(Init, M, NME1, NCE1, NMV1, NCV1),


M == vest, boat(NM, NC),
NM =< NMV1, NC =< NCV1,
NMV2 is NMV1 - NM, NCV2 is NCV1 - NC,
NME2 is NME1 + NM, NCE2 is NCE1 + NC,
safeMisionari(NMV2, NCV2),
safeMisionari(NME2, NCE2),
Next = state(est, NME2, NCE2, NMV2, NCV2).

%% -------------------------------------------------------------
%% -------------------------------------------------------------
exercitiul(3, [2, puncte]).

%% Se dau următoarele fapte ce descriu arcele unei păduri de arbori,


%% bazat pe exercițiul de la laboratorul trecut.
%% ATENȚIE: Fiecare nod poate avea acum oricâți copii.

nod(a). nod(b). nod(c). nod(d). nod(e). nod(f). nod(g).


nod(h). nod(i). nod(j). nod(k). nod(l).
nod(m).
nod(n). nod(o). nod(p). nod(q). nod(r). nod(s). nod(t). nod(u). nod(v).

arc(a, [b, c, d]). arc(c, [e, g]). arc(e, [f]).


arc(h, [i]). arc(i, [j, k, l]).
arc(n, [o, p]). arc(o, [q, r, s]). arc(p, [t, u, v]).

% TODO
% preorder/2
% preorder(+N, -Parc)
% Întoarce în Parc o listă de noduri rezultate din parcurgerea în
% preordine începând din noudl N.
preorder(N, [N | PChld]) :- arc(N, Chld), chldDfs(Chld, [], PChld).
preorder(N, [N]) :- \+ arc(N, _).

chldDfs([], Vis, P) :- reverse(Vis, P).


chldDfs([C | Chld], Vis, P) :- \+ arc(C, _),
chldDfs(Chld, [C | Vis], P).
chldDfs([C | Chld], Vis, P) :- arc(C, CChld),
chldDfs(CChld, [C | Vis], PChld),
reverse(PChld, VisChld),
chldDfs(Chld, VisChld, P).
% bfs/3
% bfs(+Q, +Vis, -Path)
bfs([], _, Path) :- Path = [].
bfs([N | Q], Vis, P) :- \+ arc(N, _), bfs(Q, [N | Vis], Pold), P = [N | Pold].
bfs([N | Q], Vis, P) :- arc(N, X), append(Q, X, NewQ),
bfs(NewQ, [N | Vis], Pold), P = [N | Pold].

exercitiul(4, [2, puncte]).


% Dată fiind funcția nodes, parcurgeți toată pădurea de arbori.

% nodes(-NN)
% Întoarce în NN toate nodurile din pădurea de arbori.
nodes(NN) :- findall(N, nod(N), NN).

% TODO
% trees/1
% trees(-Trees)
% Întoarce în trees o listă în care fiecare element este parcurgerea
% unui arbore.
trees(Trees) :- nodes(NN), sort(NN, Nodes), traverse(Nodes, [], Trees).

% traverse/3
% traverse(+Nodes, +Vis, -Trees)
traverse(NN, NN, Trees) :- Trees = [].
traverse(NN, Vis, Trees) :- nod(X), \+ member(X, Vis),
preorder(X, Tree),
append(Vis, Tree, NewVis),
sort(NewVis, SNewVis),
traverse(NN, SNewVis, NewTrees),
Trees = [Tree | NewTrees].
exercitiul(5, [3, puncte]).

% se dă un graf cu nodurile numere de la 1 la 10, și muchiile date mai


% jos.
graph(NN) :- findall(N, between(1, 10, N), NN).

edges(1, [2, 10]).


edges(3, [5, 8, 10]). edges(5, [6]). edges(4, [6, 7, 8]).
edges(8, [3, 8, 9]). edges(6, [2, 5, 9, 10]).

% TODO
% span/1
% span(-Trees)
% Întoarce o listă de arbori.
% Fiecare arbore este reprezentat prin nodul său rădăcină.
% Fiecare nod este reprezentat ca o listă care în primul element are
% valoarea nodului iar restul elementelor sunt reprezentările nodurilor
% sale copii.
% E.g. Arborele cu a rădăcină, având pe b și c copii, iar b având un
% copil d, este reprezentat ca [a, [b, [d]], [c]].
span(Trees) :- findall(N, (graph(NN), member(N, NN), edges(N, _)), NN),
dfs(NN, [], Trees).

dfs([], _, []).
dfs(N, Vis, [N]) :- graph(NN), member(N, NN), (member(N, Vis), !; \+ edges(N, _)).
dfs(N, Vis, [N | Path]) :- graph(NN), member(N, NN), edges(N, L),
dfs(L, [N | Vis], Path).
dfs([N | NN], Vis, Path) :- member(N, Vis), dfs(NN, Vis, Path).
dfs([N | NN], Vis, Path) :- dfs(N, Vis, Path1), append(Vis, Path1, Vis1),
flatten(Vis1, Vis1FLat),
dfs(NN, Vis1FLat, Path2),
Path = [Path1 | Path2].

LAB 12:

:- discontiguous exercitiul/2.

% Povestea (inspirată de Știrile de la Ora 5)


%
% În liniștitul nostru oraș s-a produs o crimă. Un individ a pătruns
% în casa unui bătrân și l-a ucis. Cadavrul a fost ascuns de către
% criminal și nu este de găsit. Este un caz complicat, dar doi
% detectivi, D1 și D2, fac cercetări și au deja o listă de
% suspecți. Știu despre fiecare dintre aceștia ce mașină are și care
% este arma lui preferată.
%
% Pentru a rezolva cazul trebuie să afle cu ce armă a fost ucisă
% victima și cu ce mașină a fugit criminalul. Din fericire, dacă se
% poate vorbi despre așa ceva în cazul unei crime îngrozitoare, nu
% există doi suspecți care să aibă aceeași mașină și aceeași armă.
%
% Cei doi detectivi se întâlnesc la secție. D1 s-a întâlnit cu un
% martor și a aflat cu ce mașină a fugit criminalul. D2 a găsit arma
% crimei. Cei doi se întâlnesc la secție, unde au următorul dialog pe
% care tu îl asculți indiscret.
%
% D1: Știu că nu știi cine-i criminalul. Nici eu nu știu.
% D2: Încă nu știu cine este.
% D1: Nici eu nu știu încă cine este criminalul.
% D1: Acum mi-am dat seama.
% D2: Mi-am dat seama și eu.
%
% Cine este criminalul?

% ----------------------------------------------------------------------------
% Mașini

conduce(aurel, ford).
conduce(bogdan, bmw).
conduce(cosmin, bmw).
conduce(daniel, ford).
conduce(eugen, bmw).
conduce(florin, dacia).
conduce(george, fiat).
conduce(horia, audi).
conduce(irina, dacia).
conduce(jean, fiat).
conduce(kiki, audi).
conduce(laura, seat).
conduce(marian, mercedes).
conduce(nicodim, opel).
conduce(ovidiu, honda).
conduce(paul, honda).

% Arme

inarmat(aurel, sabie).
inarmat(bogdan, pistol).
inarmat(cosmin, arbaleta).
inarmat(daniel, grenada).
inarmat(eugen, grenada).
inarmat(florin, sabie).
inarmat(george, pistol).
inarmat(horia, arbaleta).
inarmat(irina, pusca).
inarmat(jean, cutit).
inarmat(kiki, prastie).
inarmat(laura, pusca).
inarmat(marian, cutit).
inarmat(nicodim, prastie).
inarmat(ovidiu, maceta).
inarmat(paul, sabie).

% ----------------------------------------------------------------------------
% 1. (1p) Scrieți un predicat suspect(Nume:Marca:Arma) care să fie
% adevărat pentru fiecare suspect al problemei noastre.
exercitiul(1, [1, puncte]).

suspect(Nume : Marca : Arma) :- conduce(Nume, Marca), inarmat(Nume, Arma).

% ----------------------------------------------------------------------------
% 2. (1p) Scrieți un predicat au_pusca(-ListaNume) care să fie
% adevărat atunci când ListaNume este lista cu numele tuturor celor care
% au pușcă.
exercitiul(2, [1, puncte]).

au_pusca(ListaNume) :- findall(Nume, inarmat(Nume, pusca), ListaNume).

% ----------------------------------------------------------------------------
% 3. (1p) Scrieți predicatele au_arma(+Arma, -ListaNume)
% și au_marca(+Arma, -ListaNume), care să fie adevărate atunci când
% ListaNume este lista cu numele tuturor celor care au arma de tipul
% Arma, respectiv mașina de tipul Marca.
exercitiul(3, [1, puncte]).

au_arma(Arma, ListaNume) :- findall(Nume, inarmat(Nume, Arma), ListaNume).

au_marca(Marca, ListaNume) :- findall(Nume, conduce(Nume, Marca), ListaNume).

% ----------------------------------------------------------------------------
% 4. (1.5p) Scrieți un predicat arme_bmw(ListaArme) care să fie adevărat
% atunci când ListaArme reprezintă mulțimea tuturor armelor deținute
% de conducători de bmw.
exercitiul(4, [1.5, puncte]).

arme_bmw(ListaArme) :- au_marca(bmw, ListaNume),


findall(Arma, (member(Nume, ListaNume), inarmat(Nume, Arma)), ListaArme).

% ----------------------------------------------------------------------------
% 5. (1.5p) Scrieți un predicat arme_marca(Marca, ListaArme) care să
% fie adevărat atunci când ListaArme reprezintă mulțimea tuturor
% armelor deținute de conducători de mașini de tipul Marca.
exercitiul(5, [1.5, puncte]).

arme_marca(Marca, ListaArme) :- setof(Arma, Nume ^ (conduce(Nume, Marca), inarmat(Nume,


Arma)), ListaArme).

% ----------------------------------------------------------------------------
% 6. (2p) Scrie un predicat marci_arma_unica(ListaMarci) care să afișeze
% lista mașinilor pentru care lista armelor pe care le dețin
% conducătorii unei mărci conține un singur element. Hint: folosiți-vă
% de rezolvarea exercițiului 5. Nu folosiți length/2.
exercitiul(6, [2, puncte]).
marci_arma_unica(ListaMarci) :- findall(Marca, (conduce(_, Marca), arme_marca(Marca, [_])),
ListaMarci).

% ----------------------------------------------------------------------------
% ----------------------------------------------------------------------------

% Să revenim la secție de poliție unde tu tragi cu urechea la dialogul


% dintre cei doi detectivi:
%
% Prima replică:
% Detectiv A : Știam că nu știi cine-i criminalul.
%
% Ce înseamnă asta? Detectivul A știe mașina cu care a fugit
% suspectul. Această marcă de mașină este condusă de suspecți care
% mânuiesc diferite arme. Dacă măcar una dintre aceste arme ar fi
% aparținut doar unui singur suspect, atunci Detectivul B ar fi putut
% ști care este soluția acestui caz.
%
% Ce soluții eliminăm?
%
% Dacă Detectivul A ar fi aflat că adevăratul criminal a condus o
% Honda, atunci ar fi existat două soluții posibile:
%
% ovidiu - honda - maceta
% paul - honda - sabie
%
% Dar cum nu există decât un singur individ care are macetă (Ovidiu),
% Detectivul A nu ar fi putut afirma că Detectivul B nu poate ști.
%
% honda nu este, deci, o soluție
%
% Trebuie eliminate toate mașinile care sunt "în pereche" cu arme
% pentru care nu există mai multe posibilități.

% ----------------------------------------------------------------------------
% 7. (2.5p) Scrie un predicat suspect1/1 care este adevărat doar pentru
% numele suspecților care respectă condiția impusă de replica
% Detectivului A: niciuna dintre armele asociate cu mașina suspectului
% nu indică în mod unic un anumit individ.
exercitiul(7, [2.5, puncte]).

suspect1(Nume:Marca:Arma) :- suspect(Nume:Marca:Arma), arme_marca(Marca, ListaArme),


forall(member(A, ListaArme), au_arma(A, [_, _ | _])).

% ----------------------------------------------------------------------------
% A doua replică:
%
% Detectivul A: Nici eu nu știu!
%
% 8. (1.5p) Scrie un predicat suspect2/1 care este adevărat doar pentru
% numele suspecților care respectă condiția impusă de replica
% Detectivului A: marca nu indică unic un individ.
%
% Atenție: informația ce trebuie filtrată acum este cea care
% corespunde primei replici.
exercitiul(8, [1.5, puncte]).

suspect2(Nume:Marca:Arma) :- suspect1(Nume:Marca:Arma),
findall(Name, suspect1(Name:Marca:_), [_, _ |_]).
% ----------------------------------------------------------------------------
% A treia replică:
%
% Detectivul B: Nici eu nu știu!
%
% 9. (1p) Scrie un predicat suspect3/1 care este adevărat doar pentru
% numele suspecților care respectă condiția impusă de replica
% Detectivului B: arma nu identifică unic un individ.
%
% Atenție: informația ce trebuie filtrată acum este cea care
% corespunde primelor două replici.
exercitiul(9, [1, puncte]).

suspect3(Nume:Marca:Arma) :- suspect2(Nume:Marca:Arma),
findall(Name, suspect2(Name:_:Arma), [_, _ |_]).

% ----------------------------------------------------------------------------
% A patra replică:
%
% Detectivul A: Eu tot nu știu!
%
% 10. (1p) Scrie un predicat suspect4/1 care este adevărat doar pentru
% numele suspecților care respectă condiția impusă de replica
% Detectivului A.
%
% Atenție: informația ce trebuie filtrată acum este cea care
% corespunde primelor trei replici.
exercitiul(10, [1, puncte]).

suspect4(Nume:Marca:Arma) :- suspect3(Nume:Marca:Arma),
findall(Name, suspect3(Name:Marca:_), [_, _ | _]).
% ----------------------------------------------------------------------------
% A cincea replică:
%
% Detectivul B: Eu am aflat!
%
% 11. (0.5p) Scrie un predicat suspect5/1 care este adevărat doar pentru
% numele suspecților care respectă condiția impusă de replica
% Detectivului B.
%
% Atenție: informația ce trebuie filtrată acum este cea care
% corespunde primelor patru replici.
exercitiul(11, [0.5, puncte]).

suspect5(Nume:Marca:Arma) :- suspect4(Nume:Marca:Arma),
findall(Name, suspect4(Name:_:Arma), [_]).
% ----------------------------------------------------------------------------
% A șasea replică:
%
% Detectivul A: Și eu am aflat!
%
% 12. (0.5p) Scrie un predicat suspect6/1 care este adevărat doar pentru
% numele suspecților care respectă condiția impusă de replica
% Detectivului A.
%
% Atenție: informația ce trebuie filtrată acum este cea care
% corespunde primelor cinci replici.
exercitiul(12, [0.5, puncte]).

suspect6(Nume:Marca:Arma) :- suspect5(Nume:Marca:Arma),
findall(Name, suspect5(Name:Marca:_), [_]).
COD DISCUTAT LA CURS:
RACKET:

Lambda – expresii, funcții curry și uncurry


#lang racket

; funcții curry / uncurry


(display '===============curry-uncurry)(newline)

; apel uncurry
(+ 2 3) ; aplicare uncurry

; definire funcții în formă curry


(define curry+
(λ (x) ; + ca funcție curry, este funcție de 1 argument (primul operand)
(λ (y) ; întoarce altă funcție, care așteaptă al doilea operand
(+ x y)))) ; pentru a întoarce rezultatul

; apel curry
((curry+ 1) 2) ; aplicare funcție în formă curry

; adunarea ca funcție curry de 3 argumente


(define curry+3 (λ (x) (λ (y) (λ (z) (+ x y z)))))

; la ce ne folosește? -> reținerea unei funcții aplicate parțial


(curry+ 5) ; aplicare parțială -> rezultă o valoare care poate fi transmisă ca argument
; sau întoarsă de o funcție
; funcționale
(display '============funcționale)(newline)

; filter
(display '============filter)(newline)

(filter odd? '(1 2 3 4 5))

(filter (λ (x) (> (* x x) 5)) '(1 2 3 4 5))

; map
(display '============map)(newline)

(map odd? '(1 2 3 4 5))

(map (λ (x) (* x x)) '(1 2 3 4 5))

(map (λ (x) (+ x 1)) '(1 2 3 4 5)) ; incrementarea unei liste, cu funcție anonimă
(map (curry+ 1) '(1 2 3 4 5)) ; incrementarea unei liste, cu funcție curry aplicată parțial

(map + '(1 2 3 4 5) '(6 7 8 9 10) '(20 30 40 50 60)) ; adunarea a mai multe liste

(map list '(a b c d e) '(1 2 3 4 5) '(#t (2 . 3) x y z))


; -> '((a 1 #t) (b 2 (2 . 3)) (c 3 x) (d 4 y) (e 5 z))

(display '============curry-member)(newline)

; (define curry-member (λ (e) (λ (L) (member e L))))


(define curry-member (λ (e) (λ (L) (and (member e L) #t)))) ; variantă care forțează valoarea
întoarsă la #t sau #f
(map (curry-member 2) '((1 2 3 4) (5 6 7 8) (2 4 6 8) (1 3 5 7)))

(define flip (λ (f) (λ (a) (λ (b) ((f b) a))))) ; produce o funcție curry identică cu f dar cu argumentele
inversate

; sunt numerele din a doua listă sunt și în prima listă?


(map
((flip curry-member) '(2 3 5 7 11 13 17 19 23 29))
'(2 4 6 7 9 12 13 14 15))

; care dintre numerele din a doua listă sunt și în prima listă?


(filter
((flip curry-member) '(2 3 5 7 11 13 17 19 23 29))
'(2 4 6 7 9 12 13 14 15))

; apply
(display '============apply)(newline)

(apply + '(1 2 3 4 5))


; suma listei L:
(let ((L '(1 2 3))) (+ (car L) (cadr L) (caddr L))) ; știu că lista are 3 elemente
(let ((L '(1 2 3))) (apply + L)) ; nu e nevoie să știu lungimea listei L
; dar știu că + are număr variabil de argumente

; foldl / foldr
(display '============fold)(newline)

(foldl + 0 '(1 2 3 4 5))


; map, implementat cu fold
; folosesc foldr pentru asamblarea rezultatului în ordinea bună
; alternativ pot folosi reverse peste foldl
(define (map-using-fold f L)
(foldr (λ (elem Lpart)
(cons (f elem) Lpart)
) '() L))
(map-using-fold (curry+ 1) '(1 2 3 4 5))

; filter, implementat cu fold


(define (filter-using-fold f L)
(foldr (λ (elem Lpart)
(if (f elem) (cons elem Lpart) Lpart)
) '() L))
(filter-using-fold odd? '(1 2 3 4 5))

; suma elementelor impare dintr-o listă


(define (sumodd L)
(foldl
(λ (e rez)
(if (odd? e) (+ e rez) rez))
0 L))

; ======================
; older

(map apply (map curry+ '(1 2 3 4 5)) (map list '(6 7 8 9 10))) ; adunarea a două liste, în două faze
(define flipCurryMember (λ (L) (λ (e) (member e L))))
(define (intersectie M1 M2)
(filter (flipCurryMember M2) M1))

(define (anyodd?-1 L) (not (null? (filter identity (map odd? L)))))


(define (anyodd?-2 L) (member #t (map odd? L)))
(define (anyodd?-3 L) (not (null? (filter odd? L))))

PROMISIUNI:
#lang racket
(define-namespace-anchor a)
(define ns (namespace-anchor->namespace a))

;; clasic
(define prod1
(λ (x y)
(displayln "prod")
(if x (* y (+ y 1)) 0)))

(define test1
(λ (x)
(let ((y 5))
(prod1 x (and (display "y ") y)))))

(test1 #f) (test1 #t) (displayln "------ ^ direct")

;; quote + eval
(define prod2
(λ (x y)
(displayln "prod")
(if x (* (eval y ns) (+ (eval y ns) 1)) 0)))
; eval necesită un spațiu de nume în care să evalueze expresia
; aici, am dat spațiul de nume global; o soluție ar fi să transmitem și spațiul de nume ca argument

(define test2
(λ (x)
(let ((y 5))
(prod2 x (quote (and (display "y ") y))))))

;(test2 #f) (test2 #t) (displayln "------ ^ quote & eval")

;; inchidere λ
(define prod3
(λ (x y)
(displayln "prod")
(if x (* (y) (+ (y) 1)) 0)))

(define test3
(λ (x)
(let ((y 5))
(prod3 x (λ () (and (display "y ") y))))))

(test3 #f) (test3 #t) (displayln "------ ^ închideri λ")

;; promisiuni
(define prod4
(λ (x y)
(displayln "prod")
(if x (* (force y) (+ (force y) 1)) 0)))

(define test4
(λ (x)
(let ((y 5))
(prod4 x (delay (begin (display "y ") y))))))

(test4 #f) (test4 #t) (displayln "------ ^ promisiuni")

(define y 2)
(delay (and (display "y ") y)) ; -> prim rang

FLUXURI:
#lang racket
; Mihnea Muraru & Andrei Olaru

;(display "----- Operatori pe fluxuri -----\n")

(define-syntax-rule (pack expr)


;(lambda () expr)) ; închideri
(delay expr)) ; promisiuni

(define unpack
;(λ (package) (package))) ; închideri
force) ; promisiuni
; =====================================
(define-syntax-rule (stream-cons h t)
;; define-syntax-rule definește o construcție nestrictă,
;; deci apelul stream-cons nu va evalua h și t
(cons h (pack t)))

(define stream-car car)

(define (stream-cdr s)
(unpack (cdr s)))

(define stream-nil '())

(define stream-null? null?)


; ===============================

(define (stream-take s n)
(cond ((zero? n) '())
((stream-null? s) '())
(else (cons (stream-car s) (stream-take (stream-cdr s) (- n 1))))))

(define (stream-drop s n)
(cond ((zero? n) s)
((stream-null? s) s)
(else (stream-drop (stream-cdr s) (- n 1)))))

(define (stream-map f s)
(if (stream-null? s) s
(stream-cons (f (stream-car s))
(stream-map f (stream-cdr s)))))
(define (stream-filter f? s)
(cond ((stream-null? s) s)
((f? (stream-car s)) (stream-cons (stream-car s) (stream-filter f? (stream-cdr s))))
(else (stream-filter f? (stream-cdr s)))))

(define (stream-zip-with f s1 s2)


(stream-cons (f (stream-car s1) (stream-car s2))
(stream-zip-with f (stream-cdr s1) (stream-cdr s2))))

(define (stream-append s1 s2)


(if (stream-null? s1) s2
(stream-cons (stream-car s1)
(stream-append (stream-cdr s1) s2))))

(define (stream-assoc k s)
(cond
((stream-null? s) #f)
((equal? (car (stream-car s)) k) (stream-car s))
(else (stream-assoc k (stream-cdr s)))))

(define (list->stream L)
(if (null? L) stream-nil
(stream-cons (car L) (list->stream (cdr L)))))

; ===============================================
(display "----- Definiri de fluxuri -----\n")

(display "ones:\n")
;(define ones (letrec ((ones (cons 1 ones))) ones)) ; eroare
;(define (ones) (cons 1 (ones))) ;ciclu infinit
;(define ones (cons 1 (λ () ones))) ; inchideri
;(define ones (cons 1 (delay ones))) ; promisiuni
(define ones (stream-cons 1 ones))
; ATENȚIE: există și în racket definit stream-cons & co. Aici le-am suprascris.
; stream-cons din Racket întoarce un obiect de tip #<stream>

(car ones)
(cdr ones)
(equal? ones (stream-cdr ones))

; cu funcție recursivă
(define (naturalsFrom start)
(stream-cons start
(naturalsFrom (+ start 1))))
(define naturals (naturalsFrom 0))

; cu funcție recursivă în named let


(define naturals2
(let build ((n 0))
(stream-cons n (build (add1 n)))))

(define naturals3
(stream-cons 0 (stream-zip-with + ones naturals3)))

(display "naturals:\n")
(stream-take naturals 10)
(stream-take naturals2 10)
(stream-take naturals3 10)
; folosiți această definiție pentru a observa cum se construiesc elementele fluxului
(define naturalsBuild
(let build ((start 0)) ; observați evaluarea când folosim promisiuni pentru întârziere
(stream-cons
start
(build
(and (display `(build ,(+ start 1))) (+ start 1))))))

(display "naturals building:\n")


(stream-take naturalsBuild 0)
(stream-take naturalsBuild 1) ; se construiește un element în față din cauza implementării lui
stream-take:
(stream-take naturalsBuild 5) ; se apelează stream-take recursiv, care evaluează stream-cdr
(stream-take naturalsBuild 7)

(define even-naturals (stream-filter even? naturals))


(display "even naturals: ")
(stream-take even-naturals 5)

(define even-naturals2 (stream-zip-with + naturals naturals))


(stream-take even-naturals 5)

(define powers-of-2
;; definiție recursivă (explicită)
(letrec ((build (λ (start)
(stream-cons start
(build (* start 2))))))
(build 1)))
(define p-o-2
;; variantă de definire implicită
(stream-cons 1 (stream-map ((curry *) 2) p-o-2)))

(define p-o-2B
;; altă variantă de definire implicită
(stream-cons 1 (stream-zip-with + p-o-2 p-o-2)))

(display "Powers of 2: ")


(stream-take p-o-2 12)

(define primes ;; primes


(letrec ((sieve
(λ (numbers)
(let ((prime (stream-car numbers)))
(stream-cons prime
(sieve (stream-filter
(λ (n) (not (zero? (remainder n prime))))
(stream-cdr numbers)))
)))))
(sieve (naturalsFrom 2))))

(display "primes: ")


(stream-take primes 10)

(display "1000th prime: ")


(stream-car (stream-drop primes 999))
; =============================================== LAZY BFS
(display "----- Căutare leneşă în spaţiul stărilor -----\n")

;(display "Găsirea primei stări scop\n")

(define symbols '(a b c)) ;; alfabetul pentru palindroame


(define expand-string ;; pe baza lui s, mai construiește o serie de șiruri, de lungime + 1
(λ (s) (map (λ (symb) (cons symb s)) symbols)))
(define palindrome? (λ (s) (equal? s (reverse s))))
(define (n-palindrome? n) (λ (s) (and (equal? n (length s)) (palindrome? s)))) ; curry

(define BFS-one ;; găsește prima soluție și o întoarce


(λ (init expand goal?)
(let search ((frontier (list init))) ;; stări de explorat
;(display frontiera)
(if (null? frontier) #f ;; am terminat de explorat și nu am găsit soluție
(let ((node (car frontier))) ;; stare curentă
(if (goal? node)
node ;; am găsit o stare scop (o soluție)
(search (append (cdr frontier) (expand node))))
;; stările nou descoperite se adaugă la sfârșit
)))
))
;; primul palindrom de lungime 3:
(BFS-one '() expand-string (n-palindrome? 3))

(define BFS ;; găsește toate soluțiile (spațiul de căutare trebuie să fie finit)
(λ (init expand goal?)
(let search ((frontier (list init))) ;; stări de explorat
;(display frontiera)
(if (null? frontier) '() ;; am terminat de explorat?
(let* ((node (car frontier)) ; stare curentă
(other-solutions ;; rezultatul căutării recursive
(search (append (cdr frontier) (expand node)))))
;; stările nou descoperite se adaugă la sfârșit
(if (goal? node)
(cons node other-solutions) ;; am găsit o stare scop (o soluție)
other-solutions)
)))
))

(define limited-expand-string ; limitează rezultatul lui expand-string la lungimea len


(λ (len) (λ (s) ; definiție curry
(filter (λ (x) (<= (length x) 3)) (expand-string s)))))
; alternativ:
;(define limited-expand-string (compose ((curry filter) (compose ((curry >=) 3) length)) expand-
string))

(define 3-palindromes (BFS '()


(limited-expand-string 3)
(n-palindrome? 3)))
(display "palindroame de lungime 3: ")
3-palindromes

(define lazy-BFS ;; produce un flux de soluții, pe baza unui spațiu de căutare potențial infinit
(λ (init expand goal?)
(let search ((frontier (list init))) ;; stări de explorat
;(display frontiera)
(if (null? frontier) stream-nil ;; am terminat de explorat?
(let ((node (car frontier))) ;; stare curentă
(if (goal? node)

;; la găsirea unei stări scop, restul căutării va fi lăsat


;; până când este necesară o nouă soluție (ceea ce va forța evaluarea lui cdr din
;; fluxul de stări, și deci evaluarea lui search)
(stream-cons node (search (append (cdr frontier) (expand node))))

;; dacă nod nu este scop, continuăm căutarea pentru a putea livra un nod scop
(search (append (cdr frontier) (expand node)))
)))
)))

(define all-palindromes
(lazy-BFS '()
expand-string
palindrome?
))
(displayln "palindroamele de la 50 la 60:")
(stream-take (stream-drop all-palindromes 50) 10)

(define levels-stream
(stream-cons '(()) ; primul nivel conține doar șirul gol
(stream-map (compose
((curry apply) append) ; facem flatten după ce
((curry map) expand-string)) ; am expandat întreg nivelul
levels-stream))) ; și formăm un nou nivel
;(stream-take levels-stream 3)
(define search-space ; forma flat pentru niveluri
(let build ((cLevel '()) (levels levels-stream))
(if (null? cLevel) (build (stream-car levels) (stream-cdr levels))
(stream-cons (car cLevel) (build (cdr cLevel) levels)))))
;(stream-take search-space 20)

(define lean-palindromes (stream-filter palindrome? search-space))


(displayln "palindroamele de la 50 la 60:")
(stream-take (stream-drop lean-palindromes 50) 10)

TIPURI IN HASKELL:
module C7 where

-- construim un tip de date ca alias al unui alt tip


type ListInt = [Int]

-- construim un tip de date nou, ca enumerare de valori


data FuzzyBoolean = CertainTrue | CertainFalse | Fuzzy

-- construim un nou tip de date nou folosind constructori de bază.


data Natural = Zero
| Succ Natural
deriving (Show, Eq) -- putem afișa la consolă și putem face ==

unu = Succ Zero


doi = Succ unu

--- important: vezi utilizare constructori de date în pattern-matching


addNat Zero n = n
addNat (Succ m) n = Succ $ addNat m n

{- rulați
addNat (Succ (Succ doi)) (Succ (Succ (Succ Zero)))
addNat unu doi
addNat doi Zero == doi
:t addNat
-}

{- exemplu de expresie care nu poate fi tipată -}


--f x = x x

-- lucrare
--m :: ?
mfl
| l == [] = []
| True = f (head l) + 1 : m f (tail l)

-------------------------------------------------------

-- tree = [1, [tree]] -- > tip neuniform al elementelor din listă


-- tree = (1, [tree]) -- > tip infinit pentru ultimele două elemente din pereche

data Tree a = Nil -- arbore vid


| Leaf a -- frunză
| Node a [Tree a] -- nod cu un număr oarecare de copii

getVal Nil = undefined


getVal (Leaf x) = x
getVal (Node x _) = x

isNil Nil = True


isNil _ = False

treeOnes = Node 1 [treeOnes, treeOnes]

-- arbore binar infinit care la BFS dă lista de numere


-- mulțumesc pentru sugestia implementării unui coleg de-al vostru
treeB = mkNode 1 where
mkNode n = Node n $ map mkNode $ map (+(2*n)) [0, 1]

{- varianta de la curs:
treeB = Node 1 [stL 1, stR 1] where
stL n = Node (2*n) [stL (2*n), stR (2*n)]
stR n = let r=2*n+1 in
Node r [stL r, stR r]
-}

preord :: Tree a -> [a]


preord Nil = []
preord (Leaf x) = [x]
preord (Node x children) = x : concatMap preord children

limit :: Int -> Tree a -> Tree a


limit 0 _ = Nil
limit at t = case t of
Node x children -> Node x $ map (limit (at-1)) children
otherwise -> t
instance Show a => Show (Tree a) where
show tree = pt 0 tree where
pt level tree = case tree of
Nil -> space level ++ "- \n"
Leaf val -> space level ++ show val ++ "\n"
Node val children -> space level ++ show val ++ "\n" ++
if all isNil children then "" else
concatMap (pt $ level + 1) children
space sp = [' ' | _ <- [1..sp * 2]]
isNil Nil = True
isNil _ = False

{- încercați la consolă
limit 5 treeOnes
limit 5 treeB
take 10 $ preord treeB -- doar calea de pe partea stângă a arborelui, pentru că nu ne întoarcem
niciodată
preord $ limit 5 treeB
-}

CLASE IN HASKELL:
module PP08 where

import Debug.Trace
-- Pair 1 2
-- List [1,2,3]
l = List [Atom 1, Atom 2, List [Atom 3, Atom 4], Atom 5]

data Pair a = P a a
data NestedList a = Atom a
| List [NestedList a]
deriving Show

fromAtom (Atom x) = x
fromAtom _ = undefined

fromList (List l) = l
fromList _ = undefined

instance Show a => Show (Pair a) where


show (P x y) = "<" ++ show x ++ ";" ++
show y ++ ">"

class Invertible a where


invert :: a -> a

instance Invertible (Pair a) where


invert (P x y) = P y x

instance Invertible (NestedList a) where


invert a@(Atom _) = a
invert (List l) = List $ reverse $
map invert l

instance Invertible a => Invertible ([] a) where


invert l = reverse $ map invert l

{-- v1
class Container a where contents :: a -> [a]
instance Container [x] where
-- contents [x] = [x]
contents = id
--}

{-- v2
class Container a where contents :: a -> [b]

instance Container [x] where


contents = id
--}

class Container t where


contents :: t a -> [a]

instance Container [] where


contents l = l
instance Container Pair where
contents (P x y) = [x, y]
instance Container NestedList where
contents (Atom x) = [x]
contents (List l) =
--concatMap contents l
--concat $ map contents l
(concat . map contents) l

--{--
f1 x y z = if x == y then x else z

f2 x y = if (invert x) == (invert y)
then contents x
else contents y

f3 x y = (invert x) ++ (invert y)

f4 x y z = if x == y then z else
if x > y then x else y
--}

----------------------------
data Tree a = Nil -- arbore vid
| Leaf a -- frunză
| Node a [Tree a] -- nod cu un număr oarecare de copii

getVal Nil = undefined


getVal (Leaf x) = x
getVal (Node x _) = x

isNil Nil = True


isNil _ = False

treeOnes = Node 1 [treeOnes, treeOnes]

treeB = mkNode 1 where


mkNode n = Node n $ map mkNode $ map (+(2*n)) [0, 1]

preord :: Tree a -> [a]


preord Nil = []
preord (Leaf x) = [x]
preord (Node x children) = x : concatMap preord children

limit :: Int -> Tree a -> Tree a


limit 0 _ = Nil
limit at t = case t of
Node x children -> Node x $ map (limit (at-1)) children
otherwise -> t

instance Show a => Show (Tree a) where


show tree = pt 0 tree where
pt level tree = case tree of
Nil -> space level ++ "- \n"
Leaf val -> space level ++ show val ++ "\n"
Node val children -> space level ++ show val ++ "\n" ++
if all isNil children then "" else
concatMap (pt $ level + 1) children
space sp = [' ' | _ <- [1..sp * 2]]
isNil Nil = True
isNil _ = False

-----------------------

class TreeLike t where --t constructor de tip


makeOne :: a -> t a
toTree :: (Eq a, Show a) => (t a) -> Tree a

instance TreeLike Tree where


--makeOne v = Leaf v
makeOne = Leaf
toTree = id
instance TreeLike NestedList where
makeOne = Atom
toTree (Atom x) = Leaf x
toTree (List l) =
Node r $ map toTree rest
where
(r, List rest) = findRoot l
findRoot ((Atom x):t) =
(x, List t)
findRoot ((List l):t) =
(r, List $ List rest : t)
where (r, List rest) = findRoot l

{- vedeți afișarea lui l definit la început


toTree l
-}

data Graph a = G [(a, a)]

graph = G [(3, 8), (3, 10), (3, 5), (5, 11), (7, 8), (7, 11),
(8, 8), (8, 9), (11, 2), (11, 5), (11, 9), (11, 10)]

{-
nodes (G (e:g)) = [fst e, snd e] ++
filter (\n -> not (elem n [fst e, snd e])) (nodes (G g))
-}

nodes (G []) = []
nodes (G (e:g)) = let eNodes = [fst e, snd e] in
eNodes ++
filter (not . (flip elem $ eNodes)) (nodes $ G g)
outgoing v (G edges) = map snd $ filter (\(a, b) -> a == v) edges

-- toTree implementat doar pentru grafuri care pot fi parcurse integral pornind din primul nod din
prima muchie.
instance TreeLike Graph where
makeOne v = G [(v, undefined)]
toTree g@(G edges) = dfsFrom (head $ nodes g) [] where
dfsFrom v visited
| elem v visited = --trace ("Visiting " ++ show v ++ " seen " ++ show visited)
$
Nil
| otherwise = --trace ("Visiting " ++ show v ++ " seen " ++ show visited) $
case outgoing v $ G edges of
[] -> Leaf v
children -> Node v subTrees where
subTrees = foldl f [] children
f previousTrees child =
dfsFrom child (v : visited ++ concatMap preord
previousTrees)
: previousTrees

-- verificare makeOne
{-
makeOne 5 -- nu știm ce tip trebuie să întoarcă makeOne (este ambiguu)
fromAtom $ makeOne 5
preord $ makeOne 5
nodes $ makeOne 5 -- excepție (undefined), după ce a afișat totuși primul nod
head $ nodes $ makeOne 5 -- nu dă excepție mulțumită evaluării leneșe
-}

module C9 where
import Data.Char (isDigit)
import Debug.Trace

import PP08

-- o expresie aritmetică este fie un număr, fie o operație între alte 2 expresii
data Expr a = Number a | Op (Expr a) a (Expr a) deriving (Show, Eq)

---type Parser = [Char] -> (ParsRes Expr, [Char])


--- i - tipul inputului
--- r - tipul rezultatului parsării
--- Un parser prelucrează o parte din input, rezultând în rezultatul r,
--- și lasă restul inputului neprelucrat
type Parser i r = [i] -> (ParsRes r, [i])

data ParsRes r = Parsed {res :: r} -- dacă parsarea a reușit


| Failed -- dacă parsarea a eșuat (nu avem un rezultat și
-- nu a fost parsat nimic din input)
deriving (Show, Eq)

-- ne dorim un parser care Parser Char (Expr String) care să parseze expresii numerice din șiruri de
caractere

--- construiește un parser care parsează un element din input, dacă acesta respectă funcția dată
makeTokenParserF :: (i -> Bool) -> Parser i i
--makeTokenParserF f = \(h:t) -> if f h then (Parsed h, t] else (Failed, (h:t)) --sau:
makeTokenParserF _ [] = (Failed, [])
makeTokenParserF f input@(h:t) = if f h then (Parsed h, t) else (Failed, input)

--- este un parser pentru caracterul c


tokenParser c = makeTokenParserF (==c)
-- tokenParser '(' "(5+3)"

--- este un parser care la o aplicare parsează o cifră


digitParser = makeTokenParserF isDigit
-- :t digitParser

--- este un parser pentru operatori


--operatorParser = makeTokenParserF (\c -> elem c ['+', '*']) --- sau:
operatorParser = makeTokenParserF (flip elem $ ['+', '*'])
--sau: operatorParser = tokenParser '*' >|> tokenParser '+' --folosind operatorul definit mai târziu
-- :t operatorParser

{-- vedeți cum se poate folosi iterate împreună cu parserul pentru cifre
take 10 $ iterate (digitParser . snd) $ digitParser "2345"
take 20 $ map fst $ iterate (digitParser . snd) $ digitParser $ concatMap show [1..]
-}

--- produce un nou parser, care avansează în input atâta timp cât poate aplica perserul dat
--- rezultatele parsărilor individuale sunt adunate într-o listă
--- la sfârșit, asamblează lista folosind funcția process
(>*>) :: Eq b => Parser a b -> ([b] -> c) -> Parser a c
(>*>) parser process =
(\results ->
--- aplicăm process peste lista rezultatelor, închidem în Parsed,
(Parsed $ process $ map (res . fst) $ results,
--- și lăsăm ca neprelucrat ce a rămas de la ultima parsare
snd $ last results)) .

--- vezi și rularea cu iterate de mai sus


--- luăm rezultatele atâta timp cât parsarea reușește
takeWhile ((/= Failed) . fst) . iterate (parser . snd) . parser

{-
:t (>*>) digitParser
-- folosim read pentru a citi numărul dintr-un șir de caractere care conține un număr
-- folosim fromInteger pentru a indica lui read că trebuie să citim un întreg
digitParser >*> (fromInteger . read) $ "2345+6"
-}

--- parsează un număr și îl închide într-o valoare de tip Expr


numberParser = digitParser >*> Number

--- produce un nou parser care este concatenarea (compunerea) a două parsere cu rezultate de
același tip
--- ce rămâne de parsat de la primul parser va fi dat spre parsare celui de-al doilea parser
--- rezultatele parsării de la cele două parsere vor fi fuzionate într-o pereche
(>.>) :: Parser i rA -> Parser i rB -> Parser i (rA, rB)
(>.>) p1 p2 = (\(res1, rem1) -> case res1 of -- ne uităm la rezultatul parsării cu p1
Failed -> (Failed, rem1) -- dacă p1 a eșuat, eșuează
Parsed r1 ->
case p2 rem1 of -- altfel parsăm ce rămâne după parsarea cu p1
(Parsed r2, rem2) -> -- și, dacă reușește, punem rezultatele într-
o pereche
(Parsed (r1, r2), rem2)
(Failed, _) -> (Failed, rem1) -- dacă p2 a eșuat, eșuează
) . p1
{-- vedeți ce efect are concatenarea parserelor:
:t digitParser >.> digitParser
digitParser >.> digitParser $ "2345"
digitParser >.> digitParser >.> digitParser $ "2345"
digitParser >.> operatorParser $ "2+5"
numberParser >.> operatorParser >.> numberParser $ "22+55"
-}

--- produce un nou parser unde rezultatul vine din procesarea rezultatului parserului dat
(>=>) :: Parser a b -> (b -> c) -> Parser a c
(>=>) parser process = (\(res, rem) -> case res of --- dacă parsarea s-a realizat cu succes,
Parsed r -> (Parsed $ process r, rem) --- procesăm
rezultatul acesteia;
otherwise -> (Failed, rem) --- altfel, eșec
) . parser

-- alternativă pentru numberParser


-- numberParser = digitParser >*> id >=> Number

{-- observați efectul:


(numberParser >.> operatorParser >.> numberParser) >=> (\((n1, op), n2)-> Op n1 [op] n2) $
"22+55"
-}

--- produce un nou parser care ia parsarea cu succes dintre rezultatele


--- celor două parsere date, pe același input
(>|>) :: Parser a b -> Parser a b -> Parser a b
(>|>) p1 p2 input = case (res1, res2) of
(Failed, Failed) -> (Failed, input)
(Parsed _, _) -> r1
_ -> r2
where
r1@(res1, _) = p1 input
r2@(res2, _) = p2 input

--- parsează un operand, care este un număr sau o expresie între paranteze;
--- rezultatul parsării este o expresie
operandParser =
--- este paranteză deschisă, urmată de expresie, urmată de paranteză închisă
--- rezultatul parsării va fi (('(', expr), ')'), din care ne interesează doar expresia
((tokenParser '(' >.> exprParser >.> tokenParser ')') >=> (\((_, e), _) -> e))
>|>
--- sau este un număr
numberParser

--- parsează o expresie care este un număr sau o operație între doi operanzi
--- rezultatul parsării este o expresie
exprParser = (
--- este operand, operator, operand
--- rezultatul parsării este de exemplu ((expr, '+'), expr), din care producem
--- o unică expresie
(operandParser >.> operatorParser >.> operandParser)
>=> (\((e1, op), e2)-> Op e1 [op] e2 ))
>|>
--- sau este un număr
numberParser

{-
exprParser $ "123+((25+3)*5)"
res . fst . exprParser $ "123+((25+3)*5)"
-}

-- să calculăm!

instance TreeLike Expr where


makeOne = Number
toTree (Number x) = Leaf x
toTree (Op e1 op e2) = Node op [toTree e1, toTree e2]

computeExpr e = reduce fNode fLeaf 0 $ res $ fst $ exprParser e where


fLeaf = read
fNode "+" = foldl (+) 0
fNode "*" = foldl (*) 1

{--
computeExpr $ "123+((25+3)*5)"
-}

PROLOG – DIVERSE:
% Progress →
% valori de prim rang în Prolog -- predicate/valori booleene vs argumente/diverse
tipuri/liste/structuri
% ideea de relație între concepte
% exemplu cu Alice: ce concepte sunt? (lion, unicorn, days), ce relații sunt (yesterday, lies,
saysLies)

% Manual Prolog
% http://www.swi-prolog.org/pldoc/doc_for?object=manual
% http://www.swi-prolog.org/pldoc/man?section=builtin

% programul: descrie ce este adevărat, eventual în funcție de condiții.


p(1).
p(2).
p(a).
p([1,2,3]).

q(X, X) :- p(X).

t(X) :- \+ p(X). % nu va găsi soluții singur.

% putem pune predicatul să găsească soluții dacă avem un domeniu.


s(X) :- member(X, [1, 2, 3, 4, 5, a, b, c, d, e, [1], [1, 2], [1, 2, 3]]),
\+ p(X).

% Alice came across a lion and a unicorn in a forest of forgetfulness.


% Those two are strange beings.
% The lion lies every Monday, Tuesday and Wednesday
% and the other days he speaks the truth.
% The unicorn lies on Thursdays, Fridays and Saturdays,
% however the other days of the week he speaks the truth.
% Lion: Yesterday I was lying.
% Unicorn: So was I.
% Which day did they say that?

days([mon, tue, wed, thu, fri, sat, sun]).


is_next(Y, T, [Y, T|_]).
is_next(Y, T, [_|Days]) :- is_next(Y, T, Days).

yesterday(T, Y) :- days(Days), (is_next(Y, T, Days) ; Days = [T|_], reverse(Days, [Y|_])).

yesterday(mon, sun).
yesterday(tue, mon).
yesterday(wed, tue).
yesterday(thu, wed).
yesterday(fri, thu).
yesterday(sat, fri).
yesterday(sun, sat).

lies(lion, [mon, tue, wed]).


lies(unicorn, [thu, fri, sat]).

% Animal spune azi (Today) ca ieri a mintit


saysItLiedYesterday(Animal, Today) :-
yesterday(Today, Yesterday),
lies(Animal, DaysLies), % obtine DaysLies
\+ member(Today, DaysLies), % azi minte
member(Yesterday, DaysLies).

saysItLiedYesterday(Animal, Today) :-
yesterday(Today, Yesterday),
lies(Animal, DaysLies),
member(Today, DaysLies),
\+ member(Yesterday, DaysLies).

% solutia
sol(Today) :- saysItLiedYesterday(lion, Today), saysItLiedYesterday(unicorn, Today).
% 0. o funcție simplă pe liste
% ==========================================

% myMember(+Elem, +Lista)
myMember(_, []) :- fail. % lista vida nu poate contine elementul. Aceasta linie poate sa lipseasca.
% myMember(E, [H|_]) :- E == H.
myMember(E, [E|_]). % elementul este primul element din lista
myMember(E, [H|T]) :- E \= H, myMember(E, T). % elementul nu este primul din lista.

myMember2(X, [H|T]) :- X=H ; myMember2(X, T). % folosind 'sau'

% 1. liste; direcția construirii soluțiilor


% ==========================================

% extragere elemente din listă folosind pattern-matching


head([H|_], H).
first_two([E1, E2 | _], E1, E2).
% etc

% o prelucrare simplă pe liste


% sumList(+L, -Suma)
sumList([], 0).
sumList([H|T], S) :- sumList(T, ST), S is ST + H.

inc(X, Y) :- Y is X + 1.
% doresc ceva echivalent cu map (+1) list

% Simplu - recursivitate pe stivă (se mai face o unificare la


% întoarcere, în argumentul de ieșire).
% incListS(+LI, -LO)
incListS([], []).
incListS([H|T], LOut) :- inc(H,H1), incListS(T,T1), LOut = [H1 | T1].
% echivalent cu
% incListS([H | T],[H1 | T1]) :- inc(H, H1), incListS(T, T1).

% Cu acumulator și construcția rezultatului pe avansul în recursivitate.


% Recursivitate pe coada
% incListT(+LI, -LO)
incListT(L,LR) :- incListTH(L,[],LR).

% incListTH(+LI, +Acc, -LO)


incListTH([], L, LOut) :- reverse(L, LOut).
incListTH([H|T], LA, LR) :- inc(H,H1), incListTH(T, [H1 | LA], LR).

% 2. debugging:
% ==============================================

% folosire predicates trace, notrace (eventual debug și nodebug):


% guitracer/0 pentru activarea debuggerului grafic
% noguitracer/0 pentru dezactivare.

% ?- trace, predicat(args), nodebug.


% vedeți și http://www.swi-prolog.org/pldoc/man?section=debugoverview
% pentru predicate de 1 argument
dotracing(P, X) :- trace, call(P, X), nodebug.

% 3. variabile neinstanțiate
% ==========================================

% any(X).
any(X) :- display(X).
% sau: any(X) :- format("X este: ~w~n", [X]).
any(X, X).
% încercați:
% ?- any(X, Y).

% any(X, Y) :- X = Y. % identic cu any(X, X).


% any(X, Y) :- X is Y.
% încercați any(X, 1+2) cu ambele variante de implementare de mai sus.

% la consolă vad 'ce legări s-au forțat ca să fie adevarat?'.

/* de executat la consolă:

?- any(X,Y), X = 5.
X = Y, Y = 5.
?- any(X,Y), Y = 5.
X = Y, Y = 5.

?- [1,2,3] = [1,2,3].
true.

?- [1,X,Y] = [1,2,3].
X = 2,
Y = 3.

?- [1,X,Y] = [Z,2,3].
X = 2,
Y = 3,
Z = 1.

?- [1,X,Y] = Z.
Z = [1, X, Y].

?- [1,X,Y] = Z, (X,Y)=(2,3).
X = 2,
Y = 3,
Z = [1, 2, 3].

?-
*/

% 4. generarea numerelor
% ==========================================
% puterea generativă a limbajului: generarea unei liste
% la consolă:
%
% ?- member(1, [1,2,3])
% ?- member(X, [1, 2, 3]).
% ?- member(1, L).
% ?- length(L, 3), member(1, L), member(2, L), member(3, L).

/*
?- append([1,2,3], [2,3,4], L).
L = [1, 2, 3, 2, 3, 4].

?- append([1,2,3], L, [1,2,3,4,5]).
L = [4, 5].

?- 1 < 2.
true.

?- abc < adb.


ERROR: Arithmetic: `abc' is not a function
ERROR: In: ...

?- abc @< adb.


true.

?- [1,2,3] @< [1,3,4].


true.

?- [1,2,3] @< [1,2,2].


false.
?- member(X, [1,2,3,4,5]), X > 2.
X=3;
X=4;
X = 5.

*/

% findall(template = ce pun în listă, scop, listă cu acele legări


% pentru template care satisfac scopul)

/*
?- findall(X, (member(X, [1,2,3,4,5]), X > 2), Bag).
Bag = [3, 4, 5].

?- findall((X, Y), (member(X, [1,2,3,4,5]), X > 2, member(Y, [a, b, c])), Bag).


Bag = [(3, a), (3, b), (3, c), (4, a), (4, b), (4, c), (5, a), (5, b), (..., ...)].

?- findall((X, Y), (member(X, [1,2,3,4,5]), X > 3, member(Y, [a, b, c])), Bag).


Bag = [(4, a), (4, b), (4, c), (5, a), (5, b), (5, c)].

?-
*/

% doresc o funcție interval(?X) care să îmi dea pe rând soluțiile X = 0; X = 1; ... X = 10


% interval(X) :- X =< 10. % argumente insuficient instanțiate

% construim bazându-ne pe valori pentru care predicatul a fost deja


% demonstrat
interval(0).
interval(X) :- interval(Y), Y < 10, X is Y + 1. % merge la infinit după X = 10
% evităm ca apelul recursiv să fie prima condiție
interval2H(I, _, I).
interval2H(I, L, X):- I < L, I1 is I + 1,
interval2H(I1, L, X).
interval2(X) :- interval2H(0, 10, X).

% 5. cut și fail
% ==============================================

min(X, Y, M) :- X =< Y, M is X.
min(X, Y, M) :- X > Y, M is Y.

min2(X, Y, M) :- X =< Y, M = X.
min2(X, Y, M) :- X > Y, M = Y.

% Echivalent cu min2.
min3(X, Y, X) :- X =< Y.
min3(X, Y, Y) :- X > Y.

% Greșit! A doua soluție nu va fi corectă.


min4(X, Y, X) :- X =< Y.
min4(_, Y, Y).

% Corect: o singură soluție.


min5(X, Y, X) :- X =< Y, !.
min5(_, Y, Y).
% myMem(+Elem, +Lista)
myMem(_, []) :- fail. % lista vida nu poate contine elementul. Aceasta linie poate sa lipseasca.
% myMem(E, [H|_]) :- E == H.
% myMem(E, [E|_]). % elementul este primul element din lista
myMem(E, [E|_]) :- !.
myMem(E, [_|T]) :- myMem(E, T). % elementul nu este primul din lista.

este(lili, liliac).
este(fifi, papagal).

zboara(liliac).
zboara(papagal).

% utilizare cut împreună cu fail


pasare(X) :- este(X, liliac), !, fail.
pasare(liliac) :- !, fail.
pasare(X) :- este(X, Y), zboara(Y), !.
pasare(X) :- zboara(X).

% 6. afișare: predicatul format


% ==============================================

% print(_).
prettyPrintList(L) :- ppListH(L, 1).
ppListH([], N) :- format("completed, ~w elements printed.~n", [N]).
ppListH([H|T], I) :- format("element no. ~w: ~w~n", [I, H]), I1 is I + 1,
ppListH(T, I1).
% iterarea peste soluțiile unui scop
% ==========================================

% findall( (X, Y), (member(X, [1,2,3]), member(Y, [a,b,c,d])), Bag).


% ce pun în Bag ^ ce scop satisfac ^

% forall(nextG(A, B), A @< B).


% forall(nextG(A, B), ( A @< B ; format('Fals pentru ~w ~w', [A, B]))).

% filter cu findall:
% păstrează din lista Li elementele pentru care predicatul p este
% adevărat

filterP(LI, LO) :-
findall(X, % <- pune acele elemente X
( member(X, LI) % <- care sunt parte din lista LI
, % <- și
p(X) % <- pentru care pe este adevărat
), LO). % <- în lista LO
p(2).
p(4).

% putem face și filter care primește predicatul de test ca argument


filter(LI, P, LO) :-
findall(X, ( member(X, LI),
call(P, X)
), LO).
% backtracking
% ==========================================

stateG(S) :- member(S, [a,b,c,d,e,f,g]).


nextG(a, b).
nextG(a, e).
nextG(b, c).
nextG(b, d).
nextG(d, a). % recursive
nextG(e, f).
nextG(e, g).
initialG(a).
finalG(f).
finalG(g).
problemG(problem(initialG, nextG, finalG)).

% problem(initial, next, final)


extractInitialPred(P, problem(P, _, _)).
extractNextPred(P, problem(_, P, _)).
extractFinalPred(P, problem(_, _, P)).

initial(Pb, S) :- extractInitialPred(Init, Pb), call(Init, S).


next(Pb, S, SNext) :- extractNextPred(P, Pb), call(P, S, SNext).
final(Pb, S) :- extractFinalPred(P, Pb), call(P, S).

path(S, P, _) :- % o stare S se află pe calea dintre starea inițială și o stare finală


finalG(S), P = [S]. % dacă este stare finală
path(S, P, Vazut) :- % o stare S se află pe calea dintre starea inițială și o stare finală
nextG(S, S1), % dacă există o altă stare următoare S1
\+ member(S1, Vazut), % care nu este deja vizitată
path(S1, P1, [S1 | Vazut]), % și care este pe calea spre starea finală
P = [S|P1].

path(P) :- initialG(SI),
path(SI, P, [SI]).

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