Definiţia funcţiilor. Funcţii şi predicate predefinite
Scopul acestui lucrare este să explice crearea de noi funcţi şi realizarea evaluărilor condiţionate.
Recapitulând: termenii de program, procedură şi primitivă
au sensuri puţin diferite. Un program este o colecţie de proceduri,
o procedură specifică felul în care se face un calcul
iar o primitivă este o procedură predefinită de limbaj.
DEFUN permite construirea de noi proceduri
Sintaxa:
(DEFUN
(<parametru 1> <parametru 2> ... <parametru n>)
<corpul procedurii>)
DEFUN nu îşi evaluează argumentele, ci doar stabileşte o procedură care va putea fi mai târziu utilizată. Numele procedurii trebuie să fie un simbol. Lista ce urmează
după numele procedurii se numeşte listă de parametrii. Când o procedură va fi apelată împreuna cu
argumentele sale într-o expresie simbolică, valoarea fiecărui parametru din procedura va fi determinat de valoarea agrumentului corespunzator din expresie.
DEFUN |
Transoftmarea temperaturii exprimate în Fahrenheit
în grade Celsius:
(defun f-to-c (temp)
(/ (- temp 32) 1.8))
F-TO-C
|
DEFUN va returna numele funcţiei ce tocmai a fost definită, dar partea utilă se realizează prin efecte laterale. La apel valoarea argumentului devine valoarea temporară a parametrului procedurii.
Procesul de pregătire a spaţiului de memorie pentru
valorile parametrilor se numeşte LEGARE. Parametrii
sunt legaţi la apelul procedurilor.
Procesul de stabilire a unei valori se numeşte ATRIBUIRE.
LISP īntotdeauna atribuie valori parametrilor imediat după legare.
Exemplu legare |
Funcţia următoare reutrnează lista inversată a unei liste fomrate din 2 elemente:
(defun exchange (pair)
(list (cadr pair) (car pair))) ; Inversează elementele
EXCHANGE
|
Observaţie: Comentariile în LISP se realizează
cu ";". De la ";" până la sfârşitul
liniei este comentariu.
Problema 1 |
Unii sunt deranjaţ de numele primitivelor critice CAR, CDR şi CONS.
Definiţi noi proceduri my-first, my-rest şi construct care realizează acelaşi lucru. Puteţi defini şi my-second, my-third, etc.
|
Problema 2 |
Definiţi rotate-left care primeşte o listă ca argument şi returnează o nouă listă în care primul element al listei de intrare este ultimul element al listei returnate.
(rotate-left '(a b c))
(b c a)
(rotate-left (rotate-left '(a b c)))
(c a b)
|
Problema 3 |
Definiţi rotate-right.
|
Problema 4 |
Un palindrom este o listă care conţine aceeaşi secvenţă de elemente atunci când este citită atât de la stânga la dreapta cât şi de la dreapta la stânga. Definiţi palindrom care primeşte o listă ca argument şi returnează un palindrom de lungime dublă.
|
Problema 5 |
Definiţi ec2 care primeşte trei parametrii a, b si c şi returnează o listă ce conţine cele două rădăcini ale ecuaţiei de gradul doi a*x^2+b*x+c=0, folosind formula:
-b+-sqrt(b*b-4*a*c)
x= -----------------------------
2*a
|
Predicate
PREDICATELE sunt proceduri ce returnează valoare
de adevăr.
Definiţii mai complicate necesită folosirea unor proceduri
numite predicate. Un predicat este o procedură care returnează
o valoare ce semnalează adevarat sau fals. Fals este întotdeauna
semnalat de nil. Adevărat este deseori
semnalat de un simbol special t, dar practic
orice diferit de nil semnalează adevărat.
Observaţie: t şi
nil sunt simboluri speciale, valorile lor
fiind legate tot la t şi nil.
Astfel valoarea lui t este t
si valoarea lui nil e nil.
ATOM este un predicat care testează dacă
argumentul este un atom.
LISTP testează dacă argumentul este
o listă.
ATOM şi LISTP |
Dacă avem:
(setq digits '(zero unu doi trei patru cinci sase sapte opt noua))
(ZERO UNU DOI TREI PATRU CINCI SASE SAPTE OPT NOUA)
ATOM şi LISTP lucrează īn felul următor:
(atom 'digits)
t
(atom 'cinci)
t
(atom digits)
nil
(atom '(zero unu doi trei patru cinci sase sapte opt noua))
nil
(listp 'digits)
nil
(listp 'cinci)
nil
(listp digits)
t
(listp '(zero unu doi trei patru cinci sase sapte opt noua))
t
|
EQUAL ia două argumente şi returnează t dacă sunt egale, altfel returnează nil:
EQUAL |
(equal digits digits)
t
(equal 'digits 'digits)
t
(equal digits '(zero unu doi trei patru cinci sase sapte opt noua))
t
(equal digits 'digits)
nil
|
Predicatul = verifică dacă două numere sunt egale.
= |
(= 3.14 2.71)
nil
(= (* 5 5) (+ (* 3 3) (* 4 4)))
t
|
Trebuie să subliniem acum o caracteristică foarte importantă:
- nil şi lista vidă ()
sunt complet echivalente: nil == (), ele
satisfac predicatul EQUAL:
- prin convenţie lista vidă este tiparită ca
nil.
- pot apare erori de programare din cauză că(atom
'()) este t. Această deoarece
nil este considerat atom.
Lista vidă este singura expresie care este atât listă
cât şi atom. Atât (atom nil)
cât şi (listp nil) returnează
t.
nil şi () |
(equal nil '())
t
|
Predicatul NULL verifică daca argumentul este lista vidă:
NULL |
(null '())
t
(null t)
nil
(null digits)
nil
(null 'digits)
nil
|
Predicatul MEMBER testează dacă
un atom este elementul unei liste. MEMBER
verifcă apartanenţa doar pe primul nivel a listei.
(member <atom> <lista>)
Ar fi natural ca member să returneze t
dacă atomul aparţine listei. De fapt member returnează
fragmentul din lista care īncepe cu atomul găsit. Ideea este de
a returna ceva care este diferit de nil şi
ar putea fi util mai departe.
MEMBER |
(member 'cinci digits)
(cinci sase sapte opt noua)
(member 'zece digits)
nil
Primul argument trebuie să fie un element in primul nivel al celui de al doilea argument, nu "īngropat" undeva în listă:
(member 'cinci '((zero doi patru sase opt) (unu trei cinci sapte)))
nil
În exemplul următor se exploatează faptul că MEMBER returnează ceva util:
(length (cdr (member 'cinci digits)))
4
|
Predicatul NUMBERP testează dacă
argumentul este un număr.
NUMBERP |
Pentru exemplele urmatoare vom stabili:
(setq zero 0 unu 1 doi 2 trei 3 patru 4 cinci 5 sase 6 sapte 7 opt 8 noua 9)
9
Astel:
(numberp 3.14)
t
(numberp cinci)
t
(numberp 'cinci)
nil
(numberp digits)
nil
(numberp 'digits)
nil
|
Predicatele < şi >
aşteaptă ca argumentele să fie numere şi testează
dacă sunt ordonate strict descrescător sau crescător.
Predicate de comparaţie |
(> cinci 2)
t
(> 2 cinci)
nil
(< 2 cinci)
t
(< 2 2)
nil
(> cinci patru trei doi unu)
t
(> trei unu patru)
nil
(> 3 1 4)
nil
|
Sunt definite si operatori <= şi
>= la care stricta monotonie a operatorilor
nu mai este obligatorie.
Predicatul ZEROP testează dacă argumentul este numărul zero.
ZEROP |
(zerop zero)
t
(zerop 'zero)
error
(zerop cinci)
nil
|
Predicatul MINUSP testează dacă un număr este negativ:
MINUSP |
(minusp unu)
nil
(minusp (- unu))
t
(minusp zero)
nil
|
Predicatul EVENP testează dacă un număr este par:
EVENP |
(evenp (* 9 7 5 3))
nil
|
Observaţi că majoritatea predicatelor folosesc la sfâşitul numelui P ca mnemonică pentru predicat. Exceptie face ATOM.
Problema 6 |
Definiţi propria versiune a predicatului EVENP. Puteţi folosi primitiva REM care returnează restul împarţirii întregi a două numere.
|
Problema 7 |
Definiţi predicatul PALINDROMP care testează dacă argumentul este un palindrom.
|
Problema 8 |
Definiţi predicatul NOT-REALP care ia trei parametrii şi returnează t dacă b*b-4*a*c < 0.
|
Predicate logice
NOT returnează t
doar dacă argumentul este nil. În
caz contrar returnează nil.
NOT |
(not nil)
t
(not t)
nil
(setq pets '(dog cat))
(not (member 'dog pets))
nil
(not (member tiger pets))
t
|
AND returnează diferit de nil
doar dacă toate argumentele sunt diferite de nil.
OR returnează diferit de nil dacă
cel puţin unul din argumente este diferit de nil.
Ambele iau orice număr de argumente.
AND şi OR |
(and t t nil)
nil
(or t t nil)
t
(and (member 'dog pets) (member tiger pets))
nil
(or (member 'dingo pets) (member tiger pets))
nil
|
Argumentele pentru AND şi OR nu sunt tratate în mod standard:
- AND îşi evaluează argumentele
de la stânga la dreapta. Dacă întâlneste nil
se opreşte, argumentele ramăse nu mai sunt evaluate! Altfel
AND returnează valoarea ultimului argument.
- OR îşi evalueaza argumentele
de la stânga la dreapta. Dacă īntâlneşte ceva
diferit nil se opreşte, argumentele rămase nu mai sunt evaluate!
Altfel OR returnează nil.
Ambele predicate returnează ultima valoare calculată în
caz de succes.
AND şi OR |
(and (member 'dog pets) (member 'cat pets))
(cat)
(or (member 'dog pets) (member tiger pets))
(dog cat)
|
Selectarea alternativelor. Primitiva COND
Sintaxa lui COND este:
(COND (<test 1>
<rezultat 1>)
(<test
2> <rezultat 2> )
...
(<test
n> <rezultat n> ))
Simbolul COND este urmat de un număr
oarecare de liste fiecare conţinând un test şi o expresie
de evaluat. Fiecare listă se numeste o clauză. Ideea este
de a parcurge clauze evaluând testul fiecăreia, pâna
când valoarea unui test este diferită de nil,
īn acest caz evaluânduse şi rezultatul clauzei. Există
două cazuri speciale:
- dacă testul nici unei clauze nu este diferit de nil, COND returnează nil.
- dacă clauza conţine doar testul se returnează valoarea testului.
COND |
(cond ((null L) 'EMPTY)
(t 'NOT-EMPTY))
Deasemenea se poate scrie:
(cond (L 'NOT-EMPTY)
(t 'EMPTY))
|
Observaţi că NULL şi NOT sunt practic primitive echivalente: (NOT X) == (NULL X)
Problema 9 |
Exprimaţi (abs X), (min A B) şi (max A B) cu ajutorul lui COND.
|
Problema 10 |
Exprimaţi (not U), (or X Y Z) şi (and A B C) cu ajutorul lui COND.
|
Problema 11 |
Care este valoarea următoarei forme? Argumentaţi răspunsul.
(cons (car nil) (cdr nil))
|
COND şi DEFUN |
Vrem să definim o procedură care primeşte două argumente: un atom şi o listă, şi returnează o listă având atomul pus în capul listei doar daca nu este deja în listă. Altfel returnează lista nemodificată.
(defun adjoin (item bag)
(cond ((member item bag) bag) ; este deja în listă?
(t (cons item bag)))) ; adaugă în capul listei.
adjoin
|
Observaţie: t va aparea
de multe ori ca test pentru ultima clauza a unui COND prentru a defini ce trebuie făcut dacă nici o clauză
nu a fost evaluată.
Problema 12 |
Definiţi MEDIAN-OF-THREE,
o procedură care primeşte trei argumente numerice şi
returnează pe cel din mijloc ca valoare (adică pe cel
care nu e cel mai mic şi nu e cel mai mare).
Notă: īn scrierea fiuncţiei
ajutor mare va poate da indentare corectă a codului. |
Probleme
Problema 1. Defun. My-first, my-rest.
Problema 2. Defun. Rotate-left .
Problema 3. Defun. Rotate-right.
Problema 4. Defun. Palindrom.
Problema 5. Defun. Ecuaţie de gradul doi.
Problema 6. Predicate. Evenp.
Problema 7. Predicate. Palindrom.
Problema 8. Predicate. Ecuaţie de gradul doi.
Problema 9. Cond. Funcţii generale
Problema 10. Cond. Funcţii logice
Problema 11. Evaluarea lui nil
Problema 12. Median la trei elemente |