Professional Documents
Culture Documents
LISP
Intenia acestui capitol nu este aceea de a da o descriere exhaustiv a
limbajului ci de a introduce noiunile eseniale ale Lisp-ului, care s fac
posibil scrierea de aplicaii simple la capitolele de inteligen artificial ce
urmeaz. Nu vrem ca prezentarea s urmreasc cu necesitate un anumit
dialect de Lisp. Toate exemplele noastre vor putea ns fi rulate direct n
Allegro Common Lisp, versiunea de Common Lisp creat de Franz Lisp Co
(www.fratz.com). Common Lisp (v. [Steele, Jr., 1990]) este dialectul pe care la dezvoltat Comisia de standardizare a Lisp-ului X3J13 din cadrul ANSI
(American National Standard Institute). Datorit flexibilitii deosebite a
limbajului, care parc invit la implementri aventuroase, procesul de
standardizare a fost unul de durat, dar dup anul 1990 a fost finalizat n
varianta care este acceptat astzi de cele mai multe implementri Common
Lisp.
Lisp un limbaj proiectat special pentru problema pe care o am de
rezolvat
Dac ar fi s inventm o reclam pentru promovarea limbajului Lisp, ar
trebui, probabil, s ne exprimm astfel:
Lisp este limbajul care gzduiete, cu simplitate i elegan, un
concept de prelucrare a datelor: prelucrarea simbolic. Fr a neglija
calculul numeric, limbajele simbolice au faciliti speciale de a lucra cu
simboluri nenumerice.
Lisp este limbajul care exemplific paradigma de programare
funcional. O funcie este un obiect matematic care ntoarce o
singur valoare pentru un set de valori (numii i parametri) dai n
intrare. ntr-un limbaj funcional centrale pentru programare snt
definiia de funcie, apelul i evaluarea lui, n timp ce ntr-un limbaj pur
funcional toate construciile snt exprimate ca funcii care ntorc valori
atunci cnd snt evaluate.
Lisp este limbajul care se muleaz pe problema pe care o avei de
rezolvat. Aceast afirmaie trebuie neleas n sensul ergonomiei
excepionale pe care o ofer limbajul actului de programare.
Programarea n Lisp este, pentru un cunosctor, o plcere, un
spectacol i o invitaie la creaie. Programarea n Lisp este uoar iar
productivitatea limbajului este remarcabil. Adesea un program Lisp
este mai scurt dect unul care realizeaz acelai lucru ntr-un alt limbaj.
Lisp este limbajul care se dezvolt pe msur ce rezolvai
problema. Aceast trstur provine din utilizarea macrourilor un
element cu adevrat specific Lisp-ului. Prin macrouri, care snt
secvene de cod ce suport o execuie special, se poate da nu numai
o nou interpretare noiunii de evaluare a formelor Lisp-ului, noiune
central n limbaj, ci nsi o nou sintax, nct se pot crea n acest fel
linii de cod care nu mai seamn deloc cu sintaxa obinuit a
limbajului.
Lisp este un limbaj specializat pentru prelucrarea listelor, ceea ce
se reflect n chiar numele lui (LISt Processing). Motivul pentru care
lista, o structur de date relativ nespectaculoas, poate sta la baza
unui limbaj dedicat dezvoltrii de aplicaii ntr-un domeniu att de
pretenios precum inteligena artificial este c aceast structur este
extrem de general, o list putnd nlnui nu numai simboluri precum
numere i cuvinte ci i alte liste, oferind posibilitatea reprezentrii de o
manier uniform a unor structuri orict de complicate.
Lisp este limbajul de cas al inteligenei artificiale. Caracteristicile
ce-i confer aceast calitate snt facilitatea de a procesa cu simboluri,
lista ca structur fundamental de date i mecanismele complexe de
evaluare i utilizare a macrourilor care pot duce la construcii
procesuale sofisticate i care se comport diferit n funcie de context.
Lisp incorporeaz viziunea de programare multi-strat. Implementrile
orientate-obiect ale acestui limbaj i confer toate trsturile cunoscute.
n mod particular, programarea multi-strat nseamn construcia de
straturi de definiii funcionale, ceea ce invit la o abordare bottom-up
n rezolvarea problemelor, n aa fel nct apelurile de la un nivel
superior s incorporeze definiii de funcii i construcii de pe un nivel
inferior.
sau:
(x,y) = x+y;
ntr-o astfel de notaie funciile nu au nume. Asocierea unui nume unei funcii,
n vederea crerii posibilitii de apel al lor, trebuie fcut explicit:
function f: (x) = x+2;
function g: (x,y) = x+y;
ir de caractere
tablou (cu oricte dimensiuni)
tabel de asociaie (hash table) structur de date care permite asocierea i
regsirea cu uurin a valorilor ataate simbolurilor;
pachet (sau spaiu de nume) colecii ncapsulate de simboluri. Un parser
Lisp recunoate un nume prin cutarea lui n pachetul curent;
flux de date surs de date, tipic ir de caractere, utilizat pentru canalizarea
operaiilor de intrare/ieire.
Numai datele au tipuri. O variabil n Lisp nu are definit un tip. Ea poate primi
ca valoare orice tip de dat.
Construciile limbajului
n Lisp operm cu urmtoarele categorii de obiecte: variabile,
constante, date, funcii, macro-uri i forme speciale (la acestea mai trebuie
adugate clasele i metodele n implementrile orientate-obiect).
Variabilele snt asociaii ntre simboluri utilizate n anumite spaii
lexicale ale limbajului i valori asociate acestora. Dup cum vom vedea mai
departe, exist trei moduri prin care o variabil poate s primeasc valori,
uneori chiar mai multe odat: asignarea, legarea i prin intermediul listelor de
proprieti. Dou dintre aceste moduri de a primi valori (asignarea i listele de
proprieti) snt caracteristice oricrui simbol Lisp. Ceea ce difereniez un
simbol lexical de o variabil snt numai situaiile n care variabilele pot fi legate
la valori.
Constantele snt simboluri (n general ale sistemului) care au ataate
valori ce nu pot fi modificate.
n Lisp nu exist proceduri ci numai funcii, n sensul c orice rutin
ntoarce obligatoriu i o valoare. Macro-urile snt funcii care au un mecanism
de evaluare special, n doi pai: expandarea i evaluarea propriu-zis
(prezentate n seciunea Macro-uri. Definiie, macroexpandare i evaluare).
Formele speciale snt funcii de sistem care au, n general, o sintax i un
comportament aparte.
Un pic de sintax
Urmtoarele caractere au o semnificaie special n Lisp:
( o parantez stng marcheaz nceputul unei liste;
) o parantez dreapt marcheaz sfritul unei liste;
un apostrof, urmat de o expresie e, e, reprezint o scriere condensat
pentru un apel (quote e);
; punct-virgula marcheaz nceputul unui comentariu. El nsui mpreun cu
toate caracterele care urmeaz pn la sfritul rndului snt ignorate;
ntre o pereche de ghilimele se include un ir de caractere;
\ o bar oblic stnga prefixeaz un caracter pe care dorim s-l utilizm n
contextul respectiv ca o liter i nu cu semnificaia lui uzual. De exemplu,
unde prin <nl> am notat caracterul rnd-nou (new line), pot constitui nume de
simboluri;
# un diez semnaleaz c urmtorul caracter definete modul n care trebuie
interpretat construcia care urmeaz. Cea mai important utilizare a diezului
este de a semnala o form funcional, ntr-o secven de genul: #fn, unde
fn este un nume sau o lambda expresie (definiie de funcie fr nume);
` un accent invers semnaleaz c ceea ce urmeaz este un template care
conine virgule (mai multe despre template-uri, n seciunea Despre
apostroful-stnga). Un template funcioneaz ca un program care modific
forma unui ir de obiecte Lisp;
, virgulele snt utilizate n interiorul template-urilor pentru a semnala cazuri
speciale de nlocuiri;
: dou puncte semnaleaz, n general, c urmtorul simbol trebuie privit ca
un simbol constant care se evalueaz la el nsui. n alte cazuri, dou puncte
despart numele unui pachet de numele unei variabile definite n acel pachet
(de exemplu, n user1:alpha, user1 este numele unui pachet, iar alpha
este numele unei variabile).
Implementrile uzuale de Common Lisp snt insensibile la forma
caracterelor (minuscule sau majuscule). Intern ns, formele Lisp snt
reprezentate cu majuscule, de aceea formele rezultate n urma evalurilor snt
redate n astfel de caractere.
Numerele ntregi zecimale pot fi precedate opional de un semn i
urmate opional de un punct zecimal (adugarea unui zero dup punct le
transform ns n numele reale). Numere ntregi n alte baze au sintaxa
#nnRddddd sau #nnrddddd, n care nn exprim baza iar ddddd numrul.
Bazele 2, 8 i 16 permit i o alt scriere, respectiv #b pentru #2r, #o pentru
#8r, i #x pentru #16r.
Fraciile sau numerele raionale snt reprezentate ca un raport dintre
un numrtor i un numitor, adic o secven: semn (opional), ntreg
(numrtor), caracterul /, ntreg (numitor). Reprezentarea intern i cea
tiprit este ntotdeauna a unei fracii simplificate. Dac numrtorul e notat n
alt baz dect cea zecimal, numitorul va fi interpretat n aceeai baz. O
fracie simplificabil la un ntreg este convertit automat la ntreg. Numitorul
trebuie s fie diferit de zero. Exemple de fracii:
1/2
-2/3
; numrul 1 + 2*i
; numrul i
; convertit intern la #c(0.6666667 1.3)
Ultimul caracter din acest rnd, punctul (.), este caracter de sfrit de propoziie i, deci, nu
trebuie considerat printre caracterele enunate.
car
car
cdr
pointer ctre restul
listei (a b c)
pointer ctre
primul element al
listei (a b c)
a
pointer ctre
primul element
al listei (c)
c
pointer ctre
primul element
al listei (b c)
b
ntreaga list (a b c)
e reprezentat de
aceast celul cons
lista (c) e
reprezentat de
aceast celul cons
lista (b c) e
reprezentat de
aceast celul cons
a
alpha
beta
c
beta
gamma
delta
a.
b c
b.
Excepie fac simbolurile: nil care, fiind notaia pentru lista vid ct i
pentru valoarea logic fals, se evalueaz la el nsui i t care se
evalueaz la valoare logic adevrat (true sau TRUE). Orice sexpresie diferit de nil este considerat a fi echivalent logic cu
true:
> nil
NIL
> t
TRUE
nume de form Lisp sau macro, aadar nu face parte din biblioteca de
funcii ale Lisp-ului i nici nu este o funcie definit de utilizator)
provoac, n momentul lansrii n evaluare, un mesaj de eroare de
genul UNDEFINED-FUNCTION.
face posibile toate aceste interpretri. Cea mai apropiat de accepiunea din
limbajele imperative, atribuirea, sau asignarea, unei valori este prezentat
n continuare.
Despre forma setq s-a vorbit deja informal n seciunea Evaluarea
expresiilor Lisp. Evaluarea argumentelor de pe poziiile pare i, respectiv,
asignrile se fac n ordine, de la stnga la dreapta:
(setq s1 e1 sk ek) ek|s1e1,, sk[ek]
> (setq x (a b c) y (cdr x))
(B C)
ALPHA
> (eval (+ 1 2))
3
(car
(car
(cdr
(cdr
l) e1
nil) nil
l) e2
nil) nil
> (car
A
> (car
(ALPHA
> (cdr
(B C)
> (cdr
NIL
> (cdr
B
(a b c))
((alpha beta) b c))
BETA)
(a b c))
(a))
(a . b))
Funcia last ntoarce ultima celul cons a unei liste. Dac l=(e1.(
(ek.nil))), atunci:
(last l) (ek.nil)
(last nil) nil
> (last (a b c))
(c)
Operaii cu numere
Lisp-ul are o bibliotec foarte bogat de funcii aritmetice. Dintre ele,
prezentm doar cteva n rndurile urmtoare.
Operaiile de adunare, scdere, nmulire i mprire (k 1):
(+ n1 nk) n1 + + nk
(- n) -n i dac k > 1, atunci: (- n1 nk) n1 - - nk
(* n1 nk) n1 * * nk
(/ n1 nk) n1 / / nk
> (+
6
> (-3
> (*
6
> (/
1
1 2 3)
5 (+ 3 3) 2)
1 2 3)
6 3 2)
Aceast funcie, ca i tipul de dat numr raional, ofer, aadar, o cale foarte
elegant de a opera cu fracii zecimale:
> (+ 1 (/ 2 3))
5/3
> (* (/ 2 3) (/ 3 2))
1
> (+ (/ 1 3) (/ 2 5))
11/15
2))
2))
2))
2))
Dac c este cel mai mic ntreg pozitiv i r este un ntreg pozitiv, astfel
nct n1 = (c+1) * n2 - r, atunci:
(ceiling n1 n2) c + 1, -r
>(ceiling 7 3)
3
-2
(setq x 1)
(1+ x)
x
(1- x)
x
(setq x 1)
(incf x)
x
(decf x)
x
De notat c argumentele funciilor 1+, 1-, incf i decf pot fi orice fel de
numr, inclusiv complex. n acest din urm caz incrementarea/decrementarea
se aplic numai prii reale.
Calculul valorii absolute: dac n0, atunci:
(abs n) n
Dac n<0, atunci:
(abs n) -n
> (abs (- 3 5))
2
Predicate
Predicatele snt funcii care ntorc valori de adevr.
(zerop n) t
altfel:
(zerop n) nil
Dac n1= =nk, atunci:
(= n1 nk) t
altfel:
(= n1 nk) nil
Dac n1< <nk, atunci:
(< n1 nk) t
altfel:
(< n1 nk) nil
Dac n1> >nk, atunci:
(> n1 nk) t
altfel:
(> n1 nk) nil
Dac n1 nk, atunci:
(<= n1 nk) t
altfel:
(<= n1 nk) nil
Dac n1 nk, atunci:
(>= n1 nk) t
altfel:
(>= n1 nk) nil
Dac e s i s este un simbol legat, atunci:
(boundp e) t
altfel:
(boundp e) nil
> (setq x
Z
> (boundp
T
> (boundp
T
> (boundp
T
> (boundp
NIL
y y z)
x)
x)
y)
y)
when i unless
Liste i tabele de asociaie
Listele de asociaie snt structuri frecvent folosite n Lisp pentru accesul
rapid la o dat prin intermediul unei chei. Elementele unei liste de asociaie
snt celule cons n care prile aflate n car se numesc chei i cele aflate n
cdr date. Pentru c introducerea i extragerea noilor elemente, de regul,
se face printr-un capt al listei, ele pot fi fcute s aib un comportament
analog stivelor. ntr-o astfel de structur introducerea unei noi perechi cheiedat cu o cheie identic uneia deja existent are semnificaia umbririi
asociaiei vechi, dup cum eliminarea ei poate s nsemne revenirea n
(get s sk) ek
altfel:
(get s sk) nil
n cazul n care un al treilea argument e specificat, atunci el indic
valoarea care se dorete s nlocuiasc valoarea implicit ntoars nil atunci
cnd proprietatea solicitat nu a fost setat: dac sk {s1,,sn}:
(get s sk e) e
Atribuirea valorii unei proprieti a unui simbol se face printr-un apel
setf n care pe poziia funcionalei se folosete get:
> (setf (get 'a 'p1) 'v1)
V1
> (setf (get 'a 'p2) 'v2)
V2
> (get 'a 'p1)
V1
> (get 'a 'p2)
V2
Funcii chirurgicale
Funciile chirurgicale i justific numele prin faptul c realizeaz
modificri asupra argumentele. Ele snt aadar funcii n care efectul lateral
este cel care primeaz.
Funcia nconc modific toate argumentele (fiecare de tip list) cu
excepia ultimului, realiznd o list cu toate elementele listelor componente.
Astfel, dac:
l1 = (e11.((e1k1.nil))), ln-1 = (en-1,1.((en-1,kn-1.nil))),
ln = (en1.((enkn.nil)))
atunci:
> z
(A B C D)
> (rplaca y 'beta)
(BETA E)
> z
(A B C BETA E)
Lambda expresii
O lambda-expresie ataeaz unui set de parametri formali un corp de
funcie. O lambda-expresie poate fi folosit n locul unui nume de funcie:
((lambda (s1 sk) ec1 ecn) ep1 epk) ecn | s1ep1,,
skepk, ec1,, [ecn], unbind(s1,, sk)
Proceduri uzuale de apel snt: ca prim argument al unei liste supus
evalurii, sau prin intermediul funciilor apply, funcall ori map... (a se
vedea seciunea urmtoare). La evaluare, mai nti argumentele formale ale
lambda-definiiei (s1,, sk) se leag la valorile actuale evaluate (ep1,,
epk), apoi formele corpului definiiei (ec1 ecn) snt evaluate una dup alta.
Valoarea ntoars este cea rezultat din evaluarea ultimei forme. Lambdadefiniia marcheaz un context (domeniu) lexical pentru variabilele locale.
> ((lambda(x y) (> x y)) 3 2)
Lambda-funcii recursive 2
Nu putem utiliza o lambda definiie pentru o funcie recursiv pentru c
lambda-funcia nu are nume. Pentru a asocia nume funciilor definite ca
lambda-expresii se folosete construcia labels. Forma unui apel este:
(labels (<specificaie-legare>*) <apel>*)
n care fiecare dintre specificaiile de legare trebuie s aib forma:
(<nume> <parametri> . <corp>)
adic analog unei definiii lambda. n interiorul expresiilor din labels,
<nume> va referi acum o funcie ca dup un apel:
#'(lambda <parametri> . <corp>)
> (labels ((inc (x) (1+ x)))(inc 3))
4
n exemplul urmtor primul argument al lui mapcar este o funcie recursiv:
> (defun count-instances (obj lsts)
(labels ((instances-in (lst)
(if (consp lst)
(+ (if (eq (car lst) obj) 1 0)
(instances-in (cdr lst)))
0)))
(mapcar #'instances-in lsts)))
COUNT-INSTANCES
> (count-instances 'a '((a b c) (d a r p a) (d a r) (a a)))
(1 2 1 2)
Funcia primete un obiect i o list i ntoarce o list a numrului de apariii a
obiectului n fiecare element al listei.
> (labels((dot-product (a b)
(if (or (null a)(null b))
0
(+ (* (car a)(car b)) (dot-product (cdr a)(cdr
b)))
)))
(dot-product '(1 2 3) '(10 20 30)))
140
Apelul de mai sus cuprinde o definiie recursiv a produsului scalar al doi vectori (dai
ca liste).
Funcii de coresponden
Din aceast categorie fac parte funcii care aplic o funcional asupra
argumentelor construite din listele primite ca parametri.
2
(mapcar
l1 ln)
(f
e11 en1 )
(f
e12 en2 ) e2
(f
e1
dimensiunea ieirii =
lungimea listei minime
e1k enk ) ek
Produsul scalar al doi vectori de numere este numrul egal cu suma produselor elementelor de acelai
rang din cei doi vectori: dac v1=(a1,,an), v2=(b1,,bn), atunci v1.v2=a1*b1 ++ an*bn.
(maplist
#f
l1
ln)
(f
l1
ln
) e1
(f
(cdr l1)
(cdr ln)
) e
2
(f
(cdr((cdr l1))
(cdr((cdr ln))
dimensiunea
ieirii
=
lungimea listei
minime
)
ek
Definiii de funcii
Definiia unei funcii se face cu construcia defun:
(defun s (s1 sk) e1 en) s | s(lambda(s1 sk) e1 en)
Aadar, rezultatul unei definiii de funcii este crearea unei legri ntre un
nume, recunoscut global, s, o list de variabile, considerate variabile
formale ale funciei, s1 sk i un corp al funciei secvena e1 ek.
Dei neuzual, este permis, desigur, definirea de funcii n interiorul
altor funcii. O funcie definit n interiorul altei funcii devine cunoscut
sistemului ns numai dup apelarea cel puin o dat a funciei n care a fost
ea definit:
> (defun f1 () (princ "f1"))
F1
> (defun f2() (defun f3() (princ "f3")) (princ "f2"))
F2
> (f1)
f1
"f1"
> (f2)
f2
"f2"
> (f3)
f3
"f3"
> (defun f4() (defun f5() (princ "f5")) (princ "f4"))
F4
> (f5)
Error: attempt to call `F5' which is an undefined function.
[condition type: UNDEFINED-FUNCTION]
> (f4)
f4
"f4"
> (f5)
f5
"f5"
432513185323958463075557409114262417474349347553428646576611667
797396668820291207379143853719588249808126867838374559731746136
085379534524221586593201928090878297308431392844403281231558611
036976801357304216168747609675871348312025478589320767169132448
426236131412508780208000261683151027341827977704784635868170164
365024153691398281264810213092761244896359928705114964975419909
342221566832572080821333186116811553615836546984046708975602900
950537616475847728421889679646244945160765353408198901385442487
984959953319101723355556602139450399736280750137837615307127761
926849034352625200015888535147331611702103968175921510907788019
393178114194545257223865541461062892187960223838971476088506276
862967146674697562911234082439208160153780889893964518263243671
616762179168909779911903754031274622289988005195444414282012187
361745992642956581746628302955570299024324153181617210465832036
786906117260158783520751516284225540265170483304226143974286933
061690897968482590125458327168226458066526769958652682272807075
781391858178889652208164348344825993266043367660176999612831860
788386150279465955131156552036093988180612138558600301435694527
224206344631797460594682573103790084024432438465657245014402821
885252470935190620929023136493273497565513958720559654228749774
011413346962715422845862377387538230483865688976461927383814900
140767310446640259899490222221765904339901886018566526485061799
702356193897017860040811889729918311021171229845901641921068884
387121855646124960798722908519296819372388642614839657382291123
125024186649353143970137428531926649875337218940694281434118520
158014123344828015051399694290153483077644569099073152433278288
269864602789864321139083506217095002597389863554277196742822248
757586765752344220207573630569498825087968928162753848863396909
959826280956121450994871701244516461260379029309120889086942028
510640182154399457156805941872748998094254742173582401063677404
595741785160829230135358081840096996372524230560855903700624271
243416909004153690105933983835777939410970027753472000000000000
000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000
>
Recursivitate coad4
O funcie se spune c este coad-recursiv dac, dup apelul recursiv,
nu mai are nimic de fcut. Urmtoarea funcie e coad-recursiv:
> (defun our-find-if (fn lst)
(if (funcall fn (car lst)
4
O parte din exemplele acestei seciuni snt preluate din [Graham, 1994].
(car lst)
(our-find-if fn (cdr lst))))
Forme de secveniere
progn este o form al crei scop este pur i simplu acela de a evalua
ntr-o secven o succesiune de forme Lisp n vederea ntoarcerii ultimei
valori:
(progn e1 en) en | e1,, [en]
prog1 se comport la fel ca progn, cu deosebirea c valoarea
ntoars este cea a primei forme din cuprinderea sa:
(prog1 e1 en) e1 | [e1],, en
Utiliznd formele progn i prog1 se pot realiza mai multe evaluri n
contexte sintactice n care doar o singur form este permis (ca de exemplu,
toate cele trei poziii ale argumentelor formei if). Este clar c rostul utilizrii
unui prog1 ntr-o definiie de funcie este acela de a ntoarce o valoare
nainte efecturii unor evaluri care snt importante numai prin efectele lor
laterale.
Variabile i domeniile lor
n Lisp noiunea de variabil, att de comun n alte limbaje de
programare, neleas ca asocierea dintre un nume simbolic, un tip i un
spaiu de memorie unde poate fi depozitat o valoare, trebuie privit oarecum
diferit. Vom continua s numit variabil un simbol care apare n codul
programului cu intenia ca lui s i se asocieze valori. Mai nti, aa cum am
artat deja (v. seciunea Tipuri de date n Lisp), variabilele nu au tipuri. Apoi,
nu este cazul ca o variabil s defineasc un spaiu de memorie care s fie
ocupat de o valoare, ci simbolului care d numele variabilei i se poate asocia
o valoare.
Un numr de fome ale Lisp-ului (printre ele: defun, lambda, let, do,
dolist, dotimes etc., adic acele forme ce permit definirea de parametri
locali) creeaz domenii (sau ntinderi) ale variabilelor pe post de parametri
locali. Un domeniu este un spaiu lexical contiguu, mrginit de perechea de
(x,y) x,y,u,z
x,v
(x,z,u) x,z,u,w
PE
Forme de iterare
(acestea snt legri iar nu setri, astfel nct dup ieirea din iteraie,
variabilele revin la valorile la care erau legate nainte de intrarea n iteraie).
La nceputul fiecrei iteraii, dup procesarea variabilelor, o expresie de
test et este evaluat. Dac rezultatul este nil, execuia continu cu
evaluarea formelor din corpul do-ului: ec1,, ecq. Dac et este diferit de nil
se evalueaz n ordine formele er1,, erp, ultima valoare fiind i cea ntoars
de do.
La nceputul oricrei iteraii, cu excepia primeia, variabilele snt
actualizate astfel: toate formele de incrementare snt evaluate de la stnga la
dreapta i rezultatele snt asignate variabilelor n paralel.
n cazul formei do*, nainte de prima iteraie, evaluarea formelor de
iniializare urmat de asignarea lor variabilelor, este fcut serial pentru toate
variabilele:
(do* ((s1 ei1 es1) (sn ein esn)) (et er1 erp) ec1
ecq) erp | ei1, s1ei1,, ein, snein, while(not et){ec1,,
ecq, es1, s1es1,, esn, snesn}, er1,, [erp], unbind(s1,, sn)
Exemplele urmtoare exploateaz legrile paralele ale variabilelor de
index. n prima definiie, la fiecare pas oldx se leag la valoarea precedent
a lui x (exemple preluate din [Steele, 1990]):
(do ((x e (cdr x))
(oldx x x))
((null x))
body)
(defun list-reverse(lst)
(do ((x lst (cdr x))
(y '() (cons (car x) y)))
((endp x) y)))
((x 'a)) (prin1 x) (let ((x 'b)) (prin1 x)) (prin1 x))
((x 'a)) (prin1 x) (let () (setq x 'b) (prin1 x)) (prin1
((x 'a)) (prin1 x) (let ((x 'b)) (setq x 'c) (prin1 x))
x))
A
n primul exemplu, avem dou domenii incluse unul n altul, pentru variabila x. n
domeniul exterior x se leag la valoarea A, la intrarea n domeniul interior x se leag
la valoarea B, iar la ieirea din acesta revine la vechea valoare B.
n exemplul al doilea, x este liber n domeniul interior, dar i este asignat acolo
valoarea B. La ieirea din acest domeniu, care nu este al su, e normal ca x s
pstreze aceast valoare.
n al treilea exemplu, x este legat din nou n ambele domenii i, ulterior legrii
interioare la valoarea B, i este asignat o a treia valoare C. La revenirea n
domeniul exterior, x recapt valoarea la care era legat acolo A.
B
3
C
{result}*
nchideri5
Combinaia dintre o funcie i un set de legri de variabile libere ale
funciei la momentul apelului acelei funcii se numete nchidere (closure).
nchiderile snt funcii mpreun cu stri locale.
> (defun list+ (lst n)
(mapcar #'(lambda (x) (+ x n))
lst))
> (list+ '(1 2 3) 10)
(11 12 13)
a:
v1
F(x)
x := v2
x:
v1
n momentul apelului
x:
v2
F(a)
F(x)
x := v2
v1
x:a:
v1
n momentul apelului
x:a:
v2
Transferul n Lisp nu este nici prin valoare nici prin referin, dar poate
simula ambele tipuri: n Lisp un parametru formal se leag la valoarea
comunicat prin parametru actual. Atunci cnd, n interiorul funciei, are loc o
asignare a unei noi valori variabilei formale, ea este dezlegat de la valoarea
veche i legat la una nou:
F(a)
(defun F(x)
(setq x v2)
)
v1
n momentul apelului
v2
F(a)
(defun F(x)
(rplaca x v2)
)
v1
n momentul apelului
v2
nu satisface, pentru c la intrarea n funcie toi cei trei parametri snt evaluai.
Astfel, ntr-un apel n care am dori s atribuim variabilei x valoarea DA sau NU,
n funcie de un argument, de genul (my-if t (setq x da) (setq x nu)),
cu toate c testul se evalueaz la T, x ar fi nti setat la DA i apoi la NU, el
rmnnd n cele din urm cu aceast valoare. Apelul de funcie ntoarce ns
valoarea celui de al doilea parametru:
> (my-if t (setq x da) (setq x nu))
DA
> x
NU
Exemplele din aceast seciune i urmtoarele, pn la Cnd apare captura inclusiv, snt reproduse din
[Graham, 1994].
(memq x choices)
apelul
expandarea
definiia
Scriem apoi expansiunea dorit sub apel i unim prin linii argumentele din
corpul de apel cu poziia lor din expansiune, dar unde secvena din
expansiune ce va corespunde unicului parametru din apel este grupat, ca
aici:
(do ()
((not(not running-engine)))
(look-at-engine)
(or (ask-advice)
(think))
(try-to-fix-the-motor)
(turn-on-the-key))
(1+ x))
(1+ y))
10) (princ z) y)
x)
y))
Asupra destructurizrii
Forma destructuring-bind primete un ablon, un argument ce se
evalueaz la o list i un corp de expresii i evalueaz expresiile cu
parametrii din ablon legai la elementele corespunztoare din list:
> (destructuring-bind (x (y) . z) '(a (b) c d)
(list x y z))
(A B (C D))
1)
sau
b. e legat ntr-o parte a scheletului n care argumentele comunicate
macro-ului snt fie legate fie evaluate.
(defmacro cap2 (var)
Mai jos, simbolului animal i este atribuit o expresie care, dac este
lansat n evaluare, iniiaz un dialog ce duce la recunoaterea unui animal.
Atunci cnd recunoaterea este eronat, programul cere interlocutorului
informaii pentru rafinarea dialogului. Secvena nou de dialog astfel generat
este inserat n program astfel nct la o evaluare ulterioar arborele de
decizie al programului este mai complet. n acest fel, prin rulri repetate,
cunoaterea programului asupra diferitelor animale se perfecioneaz.
(setq animal
'(let ((int)(aniF))
(print "Animalul are singe cald? ")
(setq ras (if (read) (print "ciine") (print "lacusta")))
(print "ok? ")
(if (not (read))
(progn
(my-print (list "Puneti o intrebare la care " ras " sa
fie raspunsul pozitiv: "))
(setq int (read-line))
(print "Animalul pentru NIL: ") (setq aniF (string
(read)))
(modify animal (list 'print ras)
(list 'progn
(list 'print int)
(list 'if (list 'read) (list 'print ras) (list
'print aniF)))
)
))
))
(defun modify(str old new)
(cond ((or (atom str) (null str)) nil)
((equal (car str) old) (rplaca str new) T)
((modify (car str) old new) t)
(t (modify (cdr str) old new))))
(defun my-print (l)
(cond ((null l) (terpri))
(t (princ (car l)) (my-print (cdr l)))))
Bibliografie
Giumale, Cr., Preoescu, D., erbnai, L.D. 1987. LISP, vol. 1, Editura
Tehnic, Bucureti.
Tufi, D., Cristea, D., Tecuci, D. 1987. LISP, vol. 2, Editura Tehnic, Bucureti.
Graham, P., 1994. On Lisp. Advanced Techniques for Common Lisp. Prentice
Hall, Englewood Cliffs, New Jersey.
Steele, G. L., 1990. Common Lisp the Language, 2nd edition, Digital Press
Tufi D. 1987. TC-LISP-Funciile primitive ale interpretorului. Manual de
programare, ITCI, 98 p.
Tufi D., O. Popescu. 1987. TC-LISP-Biblioteca de funcii. Manual de
programare, ITCI, 101 p.