2014-11-03 19 views
5

Se si assegna un valore a una variabile:valore Cattura di variabili sulla creazione lambda

(setf i 10) 

e poi un creare una chiusura funzione lambda su di esso:

(setf f #'(lambda() i)) 

Abbiamo il comportamento

(incf i) ;=> 11 
(funcall f) ;=> 11 

Invece, vorrei che la funzione restituisca sempre il valore di i nel momento in cui la funzione wa s creato. Es .:

(incf i) ;=> 11 
(funcall f) ;=> 10 

Essenzialmente desidero trasformare i in un letterale all'interno del corpo lambda. È possibile farlo in Common Lisp? La ragione è che sto creando più di un lambda all'interno di un ciclo e ho bisogno di usare l'indice nei loro corpi, senza che variino dopo la creazione.

+3

I propongo di leggere un po 'di 'LET'. –

risposta

8

Basta associare una variabile con una copia del valore. Ad esempio:

(let ((i i)) 
    (lambda() i)) 

Questo è in realtà una tecnica importante con costrutti di iterazione, perché qualcosa di simile

(loop for i from 1 to 10 
    collecting (lambda() i)) 

può restituire dieci chiusure sopra le stesse variabili, per cui diventa necessario scrivere:

(loop for i from 1 to 10 
    collecting (let ((i i)) (lambda() i))) 

Se in realtà serve solo una funzione che restituisce il valore, è possibile utilizzare anche constantly (ma mi aspetto che il caso di utilizzo reale sia più complicato):

(loop for i from 1 to 10 
    collecting (constantly i)) 

L'ambiguità nei moduli di iterazione è in realtà specificata dallo standard, in alcuni casi. Ad esempio, per dotimes, dolist

Si è dipendente sia dotimes stabilisce una nuova associazione di var ad ogni iterazione o se stabilisce un attacco per var volta all'inizio e poi assegna su eventuali iterazioni successive.

Più primitivo do, tuttavia, specifica realtà che v'è una serie di attacchi per la forma, e che vengono aggiornati ad ogni iterazione (corsivo):

All'inizio di ogni iterazione diversa dalla prima, vars sono aggiornate come segue.& Hellip;

Questa ambiguità conferisce maggiore flessibilità alle implementazioni. DOLIST, per esempio potrebbe essere definita con una delle seguenti operazioni:

(defmacro dolist ((var list &optional result) &body body) 
    `(progn (mapcar #'(lambda (,var) 
         ,@(ex:body-declarations body) 
         (tagbody 
         ,@(ex:body-tags-and-statements body))) 
        ,list) 
      (let ((,var nil)) 
      ,result))) 

(defmacro dolist ((var list &optional result) &body body) 
    (let ((l (gensym (string '#:list-)))) 
    `(do* ((,l ,list (rest ,l)) 
      (,var (first ,l) (first ,l))) 
      ((endp ,l) ,result) 
     ,@body))) 
+0

Risolto! Stavo facendo il costrutto di iterazione come nel tuo secondo esempio e ottenendo tutte le chiusure con il valore dell'ultimo indice. Grazie Joshua! –

+0

@DiogoFranco Sì, c'è una certa ambiguità deliberata (comportamento dipendente dall'implementazione) riguardo a quanti moduli di associazione ci sono in alcuni costrutti di iterazioni. Ho aggiunto un po 'di più alla mia risposta per spiegare perché. –

4

Non è del tutto chiaro cosa vuoi qui. Se si desidera creare un ambito entro il quale esiste una variabile condivisa i, è possibile farlo con uno let.

CL-USER> (let ((i 10)) 
     (defun show-i() i) 
     (defun inc-i() (incf i)) 
     (defun dec-i() (decf i))) 
DEC-I 
CL-USER> (show-i) 
10 
CL-USER> (inc-i) 
11 
CL-USER> (show-i) 
11 
CL-USER> (dec-i) 
10 
CL-USER> (dec-i) 
9 
CL-USER> (show-i) 
9 
CL-USER> 

Se stai cercando di utilizzare le variabili di ambito in modo dinamico, è possibile utilizzare straight-up defvar.

CL-USER> (defvar *a* 10) 
*A* 
CL-USER> (defun show-a() *a*) 
SHOW-A 
CL-USER> (show-a) 
10 
CL-USER> *a* 
10 
CL-USER> (incf *a*) 
11 
CL-USER> (incf *a*) 
12 
CL-USER> (show-a) 
12 
CL-USER> 
+0

Oh, ma voglio l'esatto opposto. Nel mio esempio sopra, vorrei che il (funcall f) restituisse 10 per sempre, indipendentemente da cosa succedesse a "i". Sto cercando di collegare il valore di "i" quando la funzione è stata creata per il suo corpo. –

+1

@DiogoFranco Il primo di questi funzionerà per te. Basta non condividere l'ambito della copia locale di 'i' con una seconda funzione. (Se intialize il 'let' dall'esterno' i', finirai con due variabili 'i': l'originale all'esterno e una copia privata creata da' let'.) – Leushenko

4

Nel caso in cui queste due risposte non sono sufficienti a chiarezza:

(defparameter *i* 10) 
;;Before you modify *i* 
(defvar f (let ((i *i*)) 
       #'(lambda() i))) 
;;Now f will always return 10 
(funcall f) => 10 
(incf *i*) => 11 
(funcall f) => 10 
+0

Questo in realtà aggiunge un altro possibile punto di confusione, poiché '* i *' è dichiarato con 'defparameter', e quindi * globalmente * dichiarato speciale. Ciò significa che '(let ((* i * * i *)) (lambda() * i))' * non funzionerebbe *. (Questo non è lo stesso di quello che hai fatto, ma potrebbe essere utile menzionare che se rilasci una variabile speciale, ci sarebbero problemi. –

Problemi correlati