2013-06-18 10 views
5

Google Common Lisp Style Guide say Avoid modifying local variables, try rebinding insteadDalla Guida allo stile di Google Common Lisp: "Evita di modificare le variabili locali, prova invece a rebindare" il significato?

Che cosa significa? Cosa significa rebinding in quella frase?

+1

Rebinding, molto probabilmente, come nel mascherare il nome precedente con quello nuovo, ad esempio '(let ((a 1)) (let ((a 2)) ...))' –

+1

Questa è una linea guida stupida . Vai in testa e modifica le variabili locali tutto ciò che vuoi. Si noti che 'loop' modifica le variabili locali. In '(loop for i below 10 ...)', il rebinding di 'i' non ha luogo; una singola istanza di 'i' è scalinata, quindi" evitare di modificare le variabili locali "equivale a" evitare 'loop'". E quindi, con la regola dell'inferenza "reductio ad religio", dimostriamo che questa linea guida è sbagliata. – Kaz

+0

Come probabilmente sapete, lo standard Common Lisp non specifica se "LOOP" lega o meno una nuova variabile o cambia quella esistente. E lo capisco come una linea guida generale, piuttosto che una regola rigida che non deve essere infranta. –

risposta

9

Ciò significa che è necessario creare nuove variabili, invece di cambiare il valore di quelli vecchi Per esempio, prendiamo il seguente codice:

(defun foo (x) 
    (when (minusp x) 
    (setq x (- x))) 
    do something with x) 

Invece, si dovrebbe creare una nuova associazione e l'uso che uno invece:

(defun foo (x) 
    (let ((xabs (if (minusp x) 
        (- x) 
        x))) 
    do something with xabs) 

La ragione di questo è che sarete sempre sapere che cosa una variabile contiene, dal momento che non cambierà mai. Se vuoi il nuovo valore, usa semplicemente la variabile che contiene quel nuovo valore.

Ora potresti chiedere perché è così importante? Bene, alcune persone hanno una preferenza più forte per questo rispetto ad altri. Soprattutto le persone che preferiscono enfatizzare l'aspetto funzionale di Lisp sosterranno questo stile. Tuttavia, indipendentemente dalle preferenze, può essere molto utile poter sempre contare sul fatto che le variabili non cambiano. Ecco un esempio in cui questo può essere importante:

(defun foo (x) 
    (let ((function #'(lambda() (format t "the value of x is ~a~%" x)))) 
    (when (minusp x) 
     (setq x (- x))) 
    (other-function x) 
    function)) 

Quindi, il valore di ritorno di FOO è una funzione che quando viene chiamato con la stampa il valore di x. Ma il valore sarà quello di x in seguito nella funzione, il valore assoluto. Questo può essere molto sorprendente se la funzione è grande e complicata.

+0

+1. È anche interessante notare che '(let ((x x abs x)) ...)' funzionerebbe anche - non tanto la funzione 'abs', quanto il fatto che lo stesso nome di variabile possa essere usato per nuova variabile. –

+3

La chiusura che si riferisce a un valore modificato mi ricorda come in JavaScript, l'utilizzo di un ciclo per iterare su una serie di pulsanti e il collegamento di gestori onclick che fanno riferimento a un indice di iterazione è una cattiva idea –

+3

@LeCurious L'over-iterazione Il problema delle variabili si presenta anche in Common Lisp, in particolare con la [macro loop] (http://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm). Ad esempio, vedi [comportamento imprevisto con macro e chiusure loop] (http://stackoverflow.com/questions/15714537/unexpected-behavior-with-loop-macro-and-closures), che dimostra questo comportamento esatto. –

4

Non conosco Common Lisp abbastanza bene da rispondere con come farlo in Common Lisp, quindi sto usando Scheme per il mio esempio qui sotto. Supponiamo che tu stia scrivendo una funzione per restituire il fattoriale di un numero. Ecco un approccio di "modificare le variabili locali" a quella funzione (si dovrà definire il proprio while macro, ma non è difficile):

(define (factorial n) 
    (define result 1) 
    (while (> n 0) 
    (set! result (* result n)) 
    (set! n (- n 1))) 
    result) 

Ecco un approccio di "associare nuovamente variabili locali" per questa funzione:

(define (factorial n) 
    (let loop ((n n) 
      (result 1)) 
    (if (zero? n) 
     result 
     (loop (- n 1) (* result n))))) 

In questo caso, loop viene chiamato con nuovi valori da riassociare ogni volta. La stessa funzione può essere scritta utilizzando il do macro (che, tra l'altro, utilizza anche rilegatura, non modificare, almeno in Scheme):

(define (factorial n) 
    (do ((n n (- n 1)) 
     (result 1 (* result n))) 
     ((zero? n) result)))