Sunteți pe pagina 1din 4

Paradigme de Programare

Laborator 4 - Backtracking ı̂n Scheme. Continuări

by Mihai Maruseac

May 20, 2010

Vom studia o implementare generică de backtracking ı̂n Scheme s, i modul de adap-


tare a acesteia pentru problemele de căutare ı̂n spat, iul solut, iilor. La final, vom
trece ı̂n revistă mecanismul continuărilor.

Backtracking
Pentru problemele pentru care solut, ia nu este rezultatul unui simplu calcul ci se
obt, ine prin explorarea unui spat, iu al solut, iilor posibile, avem mai multe metode de
rezolvare a acestora. Putem sa folosim o căutare neinformată parcurgând graful
solut, iilor prin DFS sau BFS sau putem folosi unele informat, ii pentru a aplica algo-
ritmi precum A*, algoritmi evolutivi etc. Sau putem folosi o metodă intermediară,
cunoscută sub numele de backtracking.
Solut, ia cu backtracking are avantajul faptului că unele funct, ii vor rămâne aceleas, i
indiferent de problema analizată (partea neinformată a explorării) ı̂n timp ce
foloses, te unele informat, ii ale problemei pentru a reduce numărul de stări anali-
zate (altfel, ar fi fost un simplu DFS).
Astfel, pentru o anumită problemă, noi vom fi nevoit, i să scriem doar funct, iile
specifica problemei, nu s, i cele de parcurgere a spat, iului solut, iei. În cazul backtra-
cking, este vorba de funct, iile:
I Predicatul valid? ce testează dacă o solut, ie part, ială este validă până acum.
Dacă solut, ia nu este validă vom ies, i imediat din funct, ie returnând ’().
I Predicatul solutie? ce verifică dacă solut, ia part, ială este s, i o solut, ie a pro-
blemei. Dacă nu este solut, ie vom genera următorul nivel prin backtracking.
I Funct, ia tipar folosită pentru afis, area unei solut, ii.
Un cod general pentru backtracking ar fi următorul (ı̂n funct, ie de tipul problemei
se poate alege renunt, area la anumit, i parametri sau modificarea lor).
(define bkt
(lambda (solutie_partiala max_elem nivel max_nivel)
(if (valid? solutie_partiala)
(if (solutie? solutie_partiala nivel max_nivel)
(tipar solutie_partiala)
(foldr append ’()
(map
(lambda (i)
(bkt
(cons i solutie_partiala)
max_elem
(+ 1 nivel)
max_nivel))
(range max_elem))))
’())
))

Observăm că am folosit funct, iile generice map s, i foldr pentru a manipula fluxul
solut, iilor ı̂ntr-o manieră succintă. Prima funct, ie are rolul de a adăuga ı̂n solut, ia
part, ială de la pasul următor fiecare element de la 0 la max_elem-1. Funct, ia foldr
este folosită pentru a acumula rezultatele part, iale obt, inute pe ramura curentă.
Deoarece append are ca element neutru pe ’() s, i noi folosim lista vidă ca rezultat
ı̂n cazul ı̂n care nu avem solut, ie part, ială validă, avem garant, ia că solut, iile incorecte
nu vor apărea ı̂n rezultat.
Un exemplu de aplicare al acestei metode ar fi rezolvarea problemei permutărilor:
generarea tuturor permutărilor numerelor de la 0 la n-1. Codul este următorul:

(define valid?
(lambda (solp)
(if (null? solp) #t
(not (elem? (car solp) (cdr solp))))))

(define solutie?
(lambda (sol n N)
(= (length sol) N)))

(define perm
(lambda (n)
(bkt ’() n 0 n)))

Observăm că funct, ia solutie? nu-s, i foloses, te toate argumentele ı̂n acest caz.
Dacă am fi generat combinările, acest lucru nu ar mai fi fost valabil.

2
În final, funct, ia de afis, are a rezultatelor.

(define tipar
(lambda (sol)
(list sol)))

Rezolvarea altor probleme de backtracking se face similar.

Continuări
În programarea funct, ională tratarea cazurilor de eroare este mereu o problemă.
De exemplu, să zicem că scriem o funct, ie head ce ne va returna capul unei liste
dar vrem ca funct, ia sa funct, ioneze s, i pe liste vide (să nu arunce eroare). Nu
putem returna lista vidă sau o listă cu un mesaj de eroare pentru că ne vom
suprapune peste cazurile ı̂n care vom avea ca input o listă cu asemenea valori pe
prima pozit, ie (de exemplu ’(() 1 2 3)). Apelantul nu va s, ti să deosebească ı̂ntre
cele două cazuri dacă vom trata noi except, ia.
În schimb, putem lăsa funct, ia apelantă să-s, i definească singură ce vom face ı̂n
caz de eroare.
Matematic vorbind, având o funct, ie f : AXB → C (formă uncurry, dar metoda
funct, ionează s, i pe funct, iile curry) am putea extinde funct, ia cu ı̂ncă un argument
pentru a obt, ine fCP S : AXBX(C → D) → D. Funct, ia argument ar fi funct, ia de
tratare a erorilor s, i ar putea fi s, i ea extinsă similar.
Acest stil de programare poartă numele de Continuation Passing Style pentru
că fiecare funct, ie primes, te ca ultim argument o funct, ie ce va zice ce să se ı̂ntâmple
cu rezultatul, cum să fie el prelucrat.
Pentru a opri lant, ul de continuări se poate folosi funct, ia identity.
Ca un exemplu scurt, să arătăm cum se calculează ipotenuza unui triunghi
dreptunghic cu CPS.

(define cps+
(lambda (x y k)
(k (+ x y))))

(define cps*
(lambda (x y k)
(k (* x y))))

(define cpssqrt
(lambda (x k)
(k (sqrt x))))

3
(define cpsip
(lambda (a b k)
(cps* a a
(lambda (u)
(cps* b b
(lambda (v)
(cps+ u v
(lambda (t)
(cpssqrt t k)))))))))
Mai putem folosi continuările s, i pentru a scăpa de anumite etape ale calculului.
Este vorba de folosirea funct, iei call/cc ca ı̂n exemplul următor:
(+ 3 (call/cc (lambda(cont) (* 10 (cont 9)))))
Se va returna valoarea 12 deoarece ı̂n momentul apelării lui call/cc contextul
computat, ional ret, inea valoarea (+ 3), valoare ce se va lega la cont. În momentul
aplicării lui cont asupra lui 9 se va termina calculul s, i se va ignora ı̂nmult, irea cu
10.
O altă utilizare a acestui stil de programare o reprezintă interfet, ele grafice cu
utilizatorul(deoarece le putem folosi ca mecanisme de callback/react, ie la eveni-
mente). Scheme foloses, te clase cu CPS pentru acest lucru (ı̂n scheme o clasă se
termină ı̂n %). Pentru informat, ii suplimentare putet, i vizualiza informat, iile de la
http://docs.plt-scheme.org/gui.

Exercit, ii
1. Generat, i partit, iile unui număr natural N (descopunerile posibile ale lui N ı̂n
sume de s, iruri monoton crescătoare).
2. Generat, i toate descompunerile numărului natural N astfel ı̂ncât un termen
să fie mai mare decât anteriorul dar mai mic decât dublul lui. De exemplu
6 = 1 + 2 + 3 este o descompunere validă dar nu s, i 6 = 1 + 5.
3. Scriet, i o funct, ie ce primes, te o listă de continuări s, i un argument s, i le aplică
pe toate ı̂n ordine asupra argumentului. Se garantează că funct, iile din listă
pot fi compuse.
4. * Generat, i toate anagramele unui cuvânt astfel ı̂ncât nici o subsecvent, ă cu
lungime mai mare de 3 din cuvânt să fie palindrom.
5. *** Realizat, i sortarea prin insert, ie cu continuări.

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