Documente Academic
Documente Profesional
Documente Cultură
LISP
function foo1(x)
{ return(x+2);
}
int a=2;
int a;
function foo3(int x)
{ a := x+2;
return(a);
}
Lisp este unul dintre cele mai vechi limbaje de programare aflate nc
n larg utilizare (a aprut, n ordine, dup FORTRAN). Primele specificaii ale
limbajului au fost elaborate de John McCarthy, n 1957-58, pe baza calculului-
dezvoltat de Alonso Church i a unor dezvoltri precedente ce au aparinut
n principal lui Herbert Simon i Allen Newell.
n notaia lambda, ntr-o definiie de funcie se pun n eviden
parametrii formali ai funciei i corpul ei:
(x) = x+2;
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:
f(3)
g(5,1)
g(f(3),1) g(5,1) 6
f(3) 3 + 2 5
f(a) a + 2
ir (array)
ir de caractere
tablou (cu oricte dimensiuni)
Numai datele au tipuri. O variabil n Lisp nu are definit un tip. Ea poate primi
ca valoare orice tip de dat.
Construciile limbajului
Un pic de sintax
|&.+\:;|
a|&.+\|:;|b
a|&.+\| 2)<nl>|b
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
4/6 (echivalent cu fracia 2/3)
#o243/13 (echivalent cu 163/11)
alpha
a21
1a2
*alpha* (variabilele globale sau de sistem au, n general, nume care ncep i se
ncheie cu asterisc)
a+b
a-b+c
a/1
$13
1%2
a^b
cel_mare
a=b
a<b~c&
dudu@infoiasi
1
Ultimul caracter din acest rnd, punctul (.), este caracter de sfrit de propoziie i, deci, nu
trebuie considerat printre caracterele enunate.
Tradiional, listelor li se pot asocia notaii grafice care i au obria n
primele implementri ale Lisp-ului. Astfel, o list se noteaz ca o celul
(numit celul cons) ce conine dou jumti, prima coninnd un pointer
ctre primul element al listei, iar a doua unul ctre restul elementelor listei.
Notaia grafic a listei ca celul cons (v. figura 2) mimeaz definiia recursiv
a structurii de list: o list este format dintr-un prim element i lista format
din restul elementelor. S observm c aceast definiie se poate aplica
pentru liste nevide, adic liste care au cel puin un prim element. Tot
tradiional, prima jumtate a unei celule cons, se numete car, iar cea de a
doua cdr:
car cdr
car cdr
b c
alpha beta
beta
gamma delta
a b a b c
a. b.
Un program Lisp este format numai din apeluri de funcii. Practic, nsi
o definiie de funcie este, de fapt, tot un apel al unei funcii care creeaz o
asociere ntre un nume al funciei, o list de parametri formali i un corp al ei.
Vom presupune n cele ce urmeaz c expresiile Lisp snt interpretate
ntr-o bucl READ-EVAL-PRINT n care are loc o faz de citire a expresiei de
pe fluxul de intrare, urmat de o faz de evaluare a ei i de una de tiprire a
rezultatului. Vom presupune c prompter-ul Lisp este >. Orice expresie ce
urmeaz a fi evaluat se prezint pe un rnd imediat dup prompter, pentru ca
pe rndul urmtor s apar rezultatul tiprit al evalurii.
Expresiile se evalueaz dup cteva reguli simple:
- un atom numeric se evalueaz la el nsui:
> 3
3
> 3.14
3.14
> 9999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999
> a
alpha
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 s-
expresie diferit de nil este considerat a fi echivalent logic cu
true:
> nil
NIL
> t
TRUE
> alpha
alpha
> 3
3
> (a 3 ())
(a 3 NIL)
> (+ 2 1)
3
Primul element al listei este recunoscut drept forma Lisp setq. Forma setq
asigneaz argumentelor din apel de pe poziiile impare valorile rezultate din
evaluarea argumentelor din apel de pe poziiile pare. Astfel simbolului a i se
asigneaz rezultatul evalurii lui alpha adic alpha. Valoarea ultimului element al
listei este i valoarea ntoars de funcie. Forma setq constituie un exemplu de
funcie cu efect lateral, efect ce se manifest prin lsarea n context a unor valori
asignate unor simboluri nenumerice. Acesta este un caz n care rezultatul scontat
principal este cel dat de efectul lateral i nu de valoarea ntoars de form.
e s-expresie
n atom numeric
i numr ntreg
c numr complex
s atom nenumeric (simbolic)
l list
f funcional.
> (setq x a)
A
> (setq y b)
B
> (psetq x y y x)
NIL
> x
B
> y
A
Construcia listelor
Funcia cons construiete o celul din dou s-expresii, rezultate din
evaluarea celor dou argumente, punndu-l pe primul n jumtatea car i pe
cel de al doilea n jumtatea cdr:
(cons e1 e2) (e1.e2)
> (list a b c)
(A B C)
> (list a (b c))
(A (B C))
> (setq l1 (list 'a 'b) l2 (list 'c 'd 'e) l3 'f)
F
> (append l1 l2 l3)
(A B C D E . F)
Funcia last ntoarce ultima celul cons a unei liste. Dac l=(e1.(
(ek.nil))), atunci:
(last l) (ek.nil)
(last nil) nil
Operaii cu numere
> (+ 1 2 3)
6
> (- 5 (+ 3 3) 2)
-3
> (* 1 2 3)
6
> (/ 6 3 2)
1
> (/ 2 3)
2/3
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
> (* 99999999999999999999999999999999999999999999999999999999
8888888888888888888888888888888888888888888888888888)
888888888888888888888888888888888888888888888888888799991111111
111111111111111111111111111111111111111111112
> (floor 2)
2
0
> (floor 2.9)
2
0.9000001
> (floor 2/3)
0
2/3
> (ceiling 2)
2
0
> (ceiling 2.9)
3
-0.099999905
> (ceiling 2/3)
1
-1/3
>(floor 7 3)
2
1
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
> (complex 1 2)
#c(1 2)
Predicate
> (setq x y y z)
Z
> (boundp x)
T
> (boundp x)
T
> (boundp y)
T
> (boundp y)
NIL
> (eql 1 1)
T
Operatorii logici n Lisp snt and, or i not. Dintre acetia and i or,
pentru c i evalueaz argumentele n mod condiionat, snt considerate i
structuri de control. Funcia not are acelai comportament ca i null. Dac
e=nil, atunci:
(not e) t
altfel:
(not e) nil
> (setq n 7)
7
> (and (not (zerop n)) (/ 1 n))
1/7
> (setq n 0)
0
> (and (not (zerop n)) (/ 1 n))
NIL
Cele mai utilizate forme Lisp pentru controlul explicit al evalurii snt if
i cond. Forma if poate fi chemat cu doi sau trei parametri. n varianta cu
trei parametri, evaluarea ei se face astfel: dac e1 ~nil, atunci:
(if e1 e2 e3) e2
altfel:
(if e1 e2 e3) e3
n varianta cu doi parametri, dac: e1 ~nil, atunci:
(if e1 e2) e2
altfel:
(if e1 e2) nil
when i unless
> (setq vals (pairlis (list 'a 'b 'c) (list 1 2 3)))
((C . 3) (B . 2) (A . 1))
> (assoc 'b vals)
(B . 2)
> (rassoc 2 vals)
(B . 2)
Funcii chirurgicale
> (cons 1 2)
(1 . 2)
> (setq cons (symbol-function '+))
#<Function +>
> (funcall cons 1 2)
3
> (funcall #'max 1 2 3 4)
4
> (setq x 2)
2
> (funcall (if (> x 0) #'max #'min) 1 2 3 4)
4
> (setq x -2)
-2
> (funcall (if (> x 0) #'max #'min) 1 2 3 4)
1
Lambda expresii
Lambda-funcii recursive 2
> (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
Exemplele din aceast seciune snt preluate din [Graham, 1994].
Mapcar aplic o funcie asupra elementelor succesive ale unor liste:
dac l1 (e11.((e1k1.nil))), ln (en1.((enkn.nil))) i f
este o funcional de n argumente (aadar aritatea funcionalei este egal cu
numrul listelor comunicate ca parametri) i dac lungimea celei mai mici liste
argument este k (k = min (k1, , kn)), atunci:
numrul argumentelor =
aritatea funcionalei
(mapcar f l1 ln)
(f e11 en1 ) e1
(f e12 en2 ) e2
dimensiunea ieirii =
lungimea listei minime
(f e1k enk ) ek
Urmtorul apel realizeaz produsul scalar al doi vectori3 dai ca liste de numere:
3
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.
(cdr (assoc x l))))
'(Mihai s-a intilnit cu Mircea ca sa-l viziteze
impreuna pe Ion))
(MISU S-A INTILNIT CU MIRCEA CA SA-L VIZITEZE IMPREUNA PE
IONICA)
numrul argumentelor =
aritatea funcionalei
(maplist #f l1 ln)
(f l1 ln ) e1
dimensiunea
(f (cdr l1) (cdr ln) ) e ieirii
2
=
lungimea listei
minime
(f )
(cdr((cdr l1)) (cdr((cdr ln)) ek
Definiii de funcii
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:
Recursivitate
FACT
>(fact 1000)
402387260077093773543702433923003985719374864210714632543799910
429938512398629020592044208486969404800479988610197196058631666
872994808558901323829669944590997424504087073759918823627727188
732519779505950995276120874975462497043601418278094646496291056
393887437886487337119181045825783647849977012476632889835955735
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
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]
(x,u,w) x,v
(x,y) x,y,u,z B (x,z,u) x,z,u,w C
A
n acest apel, s1 sn snt variabile locale, ei1 ein snt expresii care,
evaluate, configureaz valorile iniiale pe care le iau variabilele nainte de
evaluarea n secven a expresiilor ec1 eck. Valoarea ntoars de let
este cea a ultimei expresii, eck.
Definiia de mai sus caut s surprind, n seciunea de efecte laterale,
secvena evalurilor, cu precdere faptul c evaluarea secvenei ec1 eck
se face n contextul iniial al legrilor variabilelor s1,, sn la valorile iniiale
ei1,, ein, legrile fiind fcute n paralel dup ce valorile iniiale au fost
evaluate serial. Aceste legri snt ns uitate la ieirea din form. ntr-adevr,
dincolo de grania acestei construcii, semnificaia variabilelor locale, ca i
legrile realizate, se pierd. Notaia noastr mai comunic i ideea c valoarea
ntoars este ntr-adevr acel eck, obinut la un anumit moment n secvena
evalurilor, dup care contextul legrilor este uitat.
Legarea simultan a variabilelor la valori face posibil schimbarea ntre
ele a valorilor a dou variabile fr intermediul unei a treia variabile care s
memoreze temporar valoarea uneia dintre ele:
Forme de iterare
dolist itereaz aceleai evaluri asupra tuturor elementelor unei
liste. Dac l = (e1.((ek.nil))), atunci:
Primul element al formei este o list definind variabilele de control ale buclei,
valorile lor de iniializare i de incrementare. Astfel, fiecrei variabile i
corespunde o list format din numele variabilei, eventual valoarea iniial i,
cnd aceasta apare, eventual o form de incrementare a pasului. Dac
expresia de iniializare e omis, ea va fi implicit luat nil. Dac expresia de
incrementare este omis, variabila nu va fi schimbat ntre pai consecutivi ai
iteraiei (dei corpul lui do poate modifica valorile variabilei prin setq).
nainte de prima iteraie, toate formele de iniializare snt evaluate i
fiecare variabil este legat la valoarea de iniializare corespunztoare
(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)
(defun list-reverse(lst)
(do ((x lst (cdr x))
(y '() (cons (car x) y)))
((endp x) y)))
> (let ((x 'a)) (prin1 x) (let ((x 'b)) (prin1 x)) (prin1 x))
ABA
A
> (let ((x 'a)) (prin1 x) (let () (setq x 'b) (prin1 x)) (prin1
x))
ABB
B
> (let ((x 'a)) (prin1 x) (let ((x 'b)) (setq x 'c) (prin1 x))
(prin1 x))
ACA
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.
Dup cum s-a putut vedea, o seam de funcii, printre care floor ct
i ceiling, ntorc valori multiple. O generare explicit de valori multiple se
poate face cu forma values-list:
nchideri5
5
Exemplele din aceast seciune snt preluate din [Graham, 1994].
Urmtoarele funcii mpart o variabil comun ce servete de
numrtor. nchiderea contorului ntr-un let n loc de a-l considera o variabil
global l protejeaz asupra referirilor accidentale.
F(a) a: v1
F(a) a: v1
F(a) a v1
F(a) a v1 a 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:
6
Exemplele din aceast seciune i urmtoarele, pn la Cnd apare captura inclusiv, snt reproduse din
[Graham, 1994].
(memq x choices) apelul
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))
>(do ((w 3)
(x 1 (1+ x))
(y 2 (1+ y))
(z))
((> x 10) (princ z) y)
(princ x)
(princ y))
Asupra destructurizrii
(defmacro foo (x y)
`(/ (+ ,x 1) ,y))
(foo (- 5 2) 6)
(/ (+ 1) )
(defmacro cap1 ()
`(+ x 1))
sau
b. e legat ntr-o parte a scheletului n care argumentele comunicate
macro-ului snt fie legate fie evaluate.
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.