Cflp

Laborator

 

Facultatea de Automatica şi Calculatoare

Departamentul de Calculatoare


 
  Lucrarea 8 Lucrarea 9 Lucrarea 10 Lucrarea 11 Lucrarea 12 Lucrarea 13 Proiect Orar


  Lucrarea 5
 

Subiecte

    

Apelul funcţiilor. Iteraţia. Lambda.

Primitivele MAPCAR şi APPLY

Un mod câteodeta mai elegant de a realiza iteraţiile este de a folosi primitivele MAPCAR şi APPLY.

MAPCAR se foloseşte atunci când dorim să aplicăm o procedură la fiecare element al unei liste. MAPCAR va returna lista rezultatelor.

Sintaxa:
    (mapcar <nume procedura>
            <lista de argumente>)

MAPCAR

(mapcar 'oddp'(1 2 3))
(t nil t)

Aici MAPCAR produce execuţia iterativă a predicatului oddp pe fiecare element al listei. Rezultatele sunt colectate într-o lista.

APPLY aplică o procedură unei liste de argumente (nu fiecaruia în parte cum face MAPCAR).

Sintaxa:
     (apply <nume procedura>
            <lista argumente>)
APPLY

Dacă avem o listă de numere

(setq l '(4 7 2))

şi dorim să adunăm numerele din lista, ar fi greşit să evaluăm

(+ l)

pentru ca

+

aşteptă argumente care sunt numere dar aici primeşte o listă de numere. De fapt am încercat să evaluăm mai sus

(+ '(4 7 2))

ceea ce evidend e greşit. Ceea ce am dori ar fi

(+ 4 7 2)

Tocmai acest lucru îl face APPLY

(apply '+ l)

13

Folosind MAPCAR şi APPLY împreuna putem rezolva mult mai elegant probleme complex recursive.

COUNT-ATOMS

O implementare mai eficientă a procedurii COUNT-ATOMS (numară atomii dintr-o listă generalizată):

(defun count-atoms (l)
      (cond ((null l) 0)
            ((atom l) 1)
            (t (apply '+
                  (mapcar 'count-atoms l)
                )))))

Aici recursivitatea nu se face doar pe două direcţii odată (car şi cdr, adică primul fiu şi frate drept), ci pe toţi fii simultan. Se face astfel o parcugere in lăţime a arborelui generalizat.

DEPTH

Următoarea procedură returnează adâncimea unui arbore generalizat.

(defun depth (l)
   (cond ((null l) 1)
         ((atom l) 0)
         (t (+ 1 (apply 'max (mapcar 'depth l))))))

MAPCAN

Primitiva MAPCAN se comportă ca şi MAPCAR combinat cu APPEND.
Sintaxa:
(MAPCAN <nume procedura> <lista argumente>)) ==
  == (APPLY 'APPEND (MAPCAR <nume procedura> <lista argumente>))

Iteraţii folosind DO

Instrucţiunea DO putem folosii asemanator ca intrucţiunule for şi do/while din limbajele de programare cunoscute
Sintaxa:
(DO ((<param 1> <valoare initala 1> <actualizare 1>)
     (<param 2> <valoare initala 2> <actualizare 2>)
     ...
     (<param n> <valoare initala n> <actualizare n>))

     (<test sfarsit> <rezultat>)
     (<corpul lui do>))

DO face şi legarea parametrilor.

Ridicare la putere întreagă

(defun expt (m n)
   (do ((result 1)               ; legare şi atribuire result
        (exponent n))            ; legare şi atr. exponent
        ((zerop exponent) result)        ; test şi return
        (setq result (* m result))       ; corp
        (setq exponent (- exponent 1)))) ; corp

sau folosind câmpul de actualizare a variabilelor:

(defun expt (m n)
    (do ((result 1 (* m result))         ; legare, iniţializare, actualizare
         (exponent n (- exponent 1)))    ; legare, iniţializare, actualizare
        ((zerop exponent) result)
          ))                             ; corp gol

Problema 1

Implementaţi urmatoarele operaţii cu mulţimi: reuniune, intersecţie, diferenţă, test de egalitate.

Problema 2

Implementaţi un program LISP care primeşte o expresie logică pe care o transformă, folosind legile lui de Morgan, într-o expresie logică folosind doar operatorul NAND.
Observaţie: Souluţia nu trebuie să fie optimizată. Exemple:

(DEMORG '(and a (not b)))
(NAND (NAND A (NAND B B) ) TRUE)
(DEMORG '(or a b c)))
(NAND (NAND A A) (NAND B B) (NAND C C))
(DEMORG '(and a (or c d) (not e)))
(NAND (NAND A (NAND (NAND C C) (NAND D D)) (NAND E E)) (NAND A (NAND (NAND C C) (NAND D D)) (NAND E E)))

LAMBDA

LAMBDA defineşte proceduri anonime.

LAMBDA

Să presupunem că avem o listă de numere şi div3 este o procedură care verifică dacă un număr este divizibil cu trei.

(setq numbers '(4 6 3 8 9 7))
(4 6 3 8 9 7)
(defun div3 (x)
     (zerop (rem x 3)))

Folosind mapcar cu procedura div3 putem verifica elementele din lista care sunt divizibile cu trei:

(mapcar 'div3 numbers)
(nil t t nil t nil)

Dacă funcţia div3 nu o mai folosim în altă parte, putem să o definim chiar în locul în care apare (mapcar poate primi nu numai numele unei funcţii ci chiar definiţia ei):

(mapcar (defun div3 (x)
                    (zerop (rem x 3)))
        numbers)
(nil t t nil t nil)

De fapt mapcar primeşte tot numele unei proceduri, pentru că defun după ce defineşte procedura returnează numele ei (vezi lucrarea 3). Observăm că numele procedurii div3 nu mai este util dacă procedura apare doar în acest loc. Ar fi util, pentru a evita proliferarea numelor inutile, ca procedura care verifică dacă un număr este divizibil cu trei să nu aibă nume (să fie anonimă). Acest lucru îl specificam printr-o primitivă asemanatoare cu defun, numită din motive istorice lambda. Deci pentru a folosi o definiţie locală anonimă scriem:

(mapcar '(lambda (x)
            (zerop (rem x 3)))
             numbers)
(nil t t nil t nil)

Primitiva lambda seamană foarte mult cu defun, doar că nu dă nume procedurii definite, putând fi folosită astfel doar în definiţii locale.
Observaţi că primitiva lambda poate să apară practic oriunde apare numele unei funcţii. În particular poate să apară pe prima poziţie a unei forme care va fi evaluată. Următoarele expresii sunt echivalente, presupunând definit div3:

(div3 6)
((lambda (x) (zerop (rem x 3))) 6)

COUNT-DIV3

Urmariţi următorul exemplu în care definim o procedură care numară câte elemente dintr-o listă sunt divizibile cu trei:

(defun count-div3 (l)
    (apply '+ (mapcar

       '(lambda (x)
          (cond ((zerop (rem x 3)) 1)
                (t 0)))
       l)))
COUNT-DIV3

(count-div3 numbers)
3

Predicatul REMOVE-IF-NOT

Predicatul remove-if-not este asemănător cu mapcar dar pastrează în rezultatul final doar elementele pentru care un anumit predicat este adevărat.

REMOVE-IF-NOT

(remove-if-not 'div3 numbers)
(6 3 9)

Şi aici putem folosi lambda:

(remove-if-not '(lambda (x)
                 (zerop (rem x 3)))
                 numbers)
(6 3 9)

Lambda interfaţează proceduri la liste de argumente.

COUNT-DEEP-DIV3

Considerăm o listă generalizată în care dorim să numărăm elementele divizibile cu un număr dat::

(defun count-deep-div (n l)
      (cond ((atom l) (cond ((zerop (rem l n)) 1)
                      (t 0)))
            (t (apply '+
                 (mapcar '(lambda (e) (count-deep-div n e))
                         l)))))

Nu puteam aplica recursiv direct (mapcar 'count-deep-div3 l) pentru ca procedura count-deep-div3 are nevoie de doi parametrii iar mapcar îi furnizează doar câte unul. Aici lambda reailzează interfaţarea dorită a procedurii.

Problema 3

Realizaţi, folosind mapcar şi lambda, o procedură count-atom care determină numărul de apariţii a unui atom într-o listă generalizată.

(count-atom 3 '(2 2 3 (4 2 4 (3) 3)4))
3

Probleme

Problema 1. Operaţii cu mulţimi.

Problema 2. Expresii logice cu NAND

Problema 3. Count-atom