Pagini

2013-05-15

Despre variabile si legaturi in Common Lisp

Notite pentru textul asta.

O variabila e un loc in care poti sa stochezi o valoare la un moment dat.
O variabila este desemnata in program printr-un identificator.
Exista o diferenta intre a crea o variabila si a schimba valoarea stocata intr-o variabila existenta, desi lucrul asta nu este evident in multe limbaje (ex. Python)
Asocierea dintre identificator si variabila (adica locul in care poti sa stochezi o valoare la un moment dat) se numeste legatura (binding).



// C
In C, legatura unei variabile este diferita de adresa ei. O legatura este o locatie de stocare. O adresa este un pointer, care e un obiect-data de prima clasa in C.
Pointerii pot fi transmisi ca argumente functiilor si returnati ca valori din functii. Legaturile nu sunt obiecte de prima clasa.
Pointerii din C par sa corespunda legaturilor, dar nu de fapt nu corespund.
// /C

Notatie: X*n desemneaza a n-a legatura a identificatorului X.

LET creeaza legaturi noi la fiecare rulare. Alte forme stabilesc legaturi in momentul compilarii, nu al rularii, ceea ce inseamna ca primesti aceleasi legaturi la fiecare rulare.

In C un identificator poate avea o singura legatura la un moment dat. In Common Lisp un identificator poate avea mai multe tipuri de legaturi simultan.

(let ((x 1))                                  ; creeaza o variabila numita X
  (flet ((x (y) (+ x y)))                 ; creeaza o functie numita X
  (x x)))                                     ; apeleaza functia X cu variabila X ca parametru

Aici numele X este asociat simultan cu o functie si o valoare. Pentru a distinge cele doua legaturi le numim legatura-valoare si legatura-functie. Exista si alte tipuri de legaturi in Common Lisp (care?)
In Common Lisp, "valoarea lui X" inseamna "valoarea legaturii-valoare a identificatorului X".

Scop ([domeniu de] vizibilitate)

Vizibilitatea unei legaturi este data de acele parti ale programului in care acea legatura este vizibila, adica in care valoarea legaturii poate fi accesata.

(defun foo()
  (let ((x 1) (y 2))                       ; legaturile X*1 si Y*1
    (frotz x y)                              ; X se refera la X*1, Y se refera la Y*1
    (let ((y 2) (z 3))                     ; stabileste legaturile Y*2 si Z*1
      (baz x y z))                         ; X*1, Y*2, Z*1
                                                ; Y*1 nu poate fi accesat aici, e ASCUNS (umbrit) de Y*2
  z))                                          ; nu se refera la Z*1, pentru ca Z*1 nu e vizibil aici

Z la ultimul acces este variabila libera, adica nu are legatura lexicala aparenta, adica nu are legatura stabilita de codul inconjurator. In mod normal asta inseamna ca daca apelam FOO primim eroare:

(foo) --> ERROR: z is unbound   ; Z nu are legatura

Putem crea o legatura pentru Z folosind, de exemplu, forma speciala DEFVAR:

(defvar z 3)                                  ; stabilim legatura Z*D1

Acum FOO nu mai da eroare:

(foo) --> 3                                  ; se refera la valoarea din Z*D1

De observat ca legatura stabilita de DEFVAR se comporta diferit de legatura stabilita de LET: cea stabilita de LET dispare atunci cand forma LET returneaza, pe cand legatura stabilita de DEFVAR continua sa ramana chiar si dupa ce DEFVAR returneaza.
In plus, faptul ca o referinta la Z se refera la legatura stabilita de DEFVAR depinde de ordinea de rulare: daca referinta la Z are loc inainte de DEFVAR, atunci e eroare, altfel se refera la legatura stabilita de DEFVAR.
DEFVAR stabileste un alt tip de legatura, dinamica (DYNAMIC), motiv pentru care ne-am referit la ea ca Z*D1 in loc de Z*1.

Vizibilitate lexicala vs dinamica

In codul urmator:

(defvar z 1)                                ; stabileste legatura Z*D1
(defun foo () z)
(defun baz ()
  (let ((z 2))                                ; stabileste legatura Z*2
    (foo)))

Ce returneaza (BAZ)? Returneaza rezultatul apelarii lui FOO, care returneaza valoarea lui Z. Care valoare? exista doua legaturi pentru Z in momentul respectiv: una creata de DEFVAR (Z*D1) si una creata de LET (Z*2). Care legatura e vizibila atunci cand FOO este apelata din BAZ? Z*2 ascunde Z*D1 in acelasi fel in care Y*2 ascundea pe Y*1?

Regula pentru legaturile create de LET este ca domeniul lor de vizibilitate este corpul lui LET, dar asta poate sa insemne doua lucruri. Poate sa insemne corpul lui LET definit de cod, sau corpul lui LET definit de calea de executie. Referinta la Z este in interiorul lui Z conform caii de executie, dar nu si conform structurii codului (deoarece codul lui FOO este in afara codului lui BAZ)


***
Experiment in Emacs Lisp, care de-abia in versiunea 24 a primit domeniu de vizibilitate lexicala, activabil prin setarea variabilei lexical-binding pe non-nil:

(let ((x 2))                             ; creez o legatura lexicala pentru identificatorul X, adica X*1
  (defun foo ()
    x))                                    ; pe care o refer in FOO => variabila X este inchisa de FOO
(foo)                                     ; eroare, (void-variable x)
(setq lexical-binding t)           ; pentru activare vizibilitate lexicala
(let ((x 2))                             ; repet
  (defun foo ()
    x))
(foo)                                     ; rezultatul e 2, a gasit legatura
(defun bar ()
z)
(bar)                                     ; eroare, (void-variable z)
(defvar z 3)                           ; creez legatura Z*D1
(bar)                                     ; returneaza 3

Din cate imi dau seama, la crearea functiei se uita dupa legatura desemnata de identificatorul furnizat de programator. Daca nu o gaseste, o face dinamica. Daca o gaseste, o face lexicala sau dinamica, in functie de cum o gaseste.
Se pot vedea diferente daca ne uitam la codul in limbaj de asamblare pentru cele doua variante:
***


***
In Lisp you can create a binding using ‘let’:

(let ((a 1)) (print a))
=> 1
This creates a fresh location, puts the value 1 there, and binds the name “a” to it.
Each use of ‘let’ creates a fresh location, even if you use the same name:

(let ((a 1))
  (let ((a 2))
    (let ((a 3))
      (print a))
    (print a))
  (print a))
=>
3
2
1
Note that there are plenty of other ways to make bindings: ‘defconst’, ‘defun’, ‘defvar’, ‘flet’, ‘labels’, ‘prog’, etc.
***
O legatura isi poate schimba valoarea stocata, in timp.

Legaturile nu sunt obiecte de prima clasa, cum sunt simbolurile; nu pot fi returnate din functii.

http://stackoverflow.com/a/7386049 (RJoswig)
Face diferenta intre variabilele create cu DEFVAR si DEFPARAMETER, ca fiind variabile globale, si variabilele definite de LAMBDA, DEFUN, LET, LET* etc, ca fiind locale.
Functiile sunt introduse in top-level cu DEFUN, DEFGENERIC si DEFMETHOD. Functiile pot fi definite si local cu FLET si LABELS. O functie cu nume e un identificator pentru o functie ("a named function is an identifier for a function in Lisp code")

DEFVAR defineste o variabila ca fiind speciala. Declaratia este globala si are efect si asupra legaturilor facute cu LET. O variabila declarata speciala ramane speciala si nu poti sa o declari lexicala ulterior.



(setq silly (append '(lambda (x)) (list (list '+ (* 3 4) 'x))))
=> (lambda (x) (+ 12 x))
This computes a list that looks like (lambda (x) (+ 12 x)) and makes it the value (not the function definition!) of silly. Here is how we might call this function:
(funcall silly 1)
=> 13
(It does not work to write (silly 1), because this function is not the function definition of silly. We have not given silly any function definition, just a value as a variable.)

Niciun comentariu:

Trimiteți un comentariu

S-ar putea sa nu vedeti comentariul aparand imediat, asta inseamna ca el asteapta aprobarea mea. Aceasta e o masura anti-SPAM.