2013-06-18 6 views
5

Google Common Lisp Przewodnik po stylu: Avoid modifying local variables, try rebinding insteadZ przewodnika Google Common Lisp Style Guide: "Unikaj modyfikowania zmiennych lokalnych, spróbuj ponownie powiązać" co oznacza?

Co to znaczy? Co oznacza ponowne wiązanie w tym zdaniu?

+1

Ponowne wiązanie, najprawdopodobniej tak jak przy maskowaniu poprzedniej nazwy na nową, np. '(Let ((a 1)) (let ((a 2)) ...))' –

+1

To jest głupia wskazówka . Idź głową i zmodyfikuj lokalne zmienne, tak jak chcesz. Zwróć uwagę, że 'loop' modyfikuje zmienne lokalne. W '(pętla dla i poniżej 10 ...)' ponowne wiązanie 'i' nie ma miejsca; pojedyncza instancja 'i' jest krokowa, więc" unikaj modyfikacji lokalnych zmiennych "jest równoznaczne z" unikaniem 'pętli". A zatem, przez zasadę wnioskowania "reductio ad religio", udowodniliśmy, że ta wskazówka jest niewłaściwa. – Kaz

+0

Jak zapewne wiesz, standard Common Lisp nie określa, czy 'LOOP' wiąże nową zmienną, czy też zmienia istniejącą. Rozumiem to raczej jako ogólną wytyczną, a nie jako surową regułę, której nie wolno łamać. –

Odpowiedz

9

oznacza to, że należy utworzyć nowe zmienne zamiast zmianę wartości starych. Na przykład, weźmy następujący kod:

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

Zamiast tego, należy utworzyć nowe wiązania i używać tego jednego Zamiast:

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

Powodem tego jest to, że będziesz zawsze wiedzieć, co zmienna zawiera, ponieważ nigdy się nie zmieni. Jeśli chcesz nową wartość, po prostu użyj zmiennej, która przechowuje tę nową wartość.

Teraz możesz zapytać, dlaczego to jest tak ważne? Cóż, niektórzy ludzie mają silniejsze preferencje niż inni. Szczególnie osoby, które wolą podkreślać funkcjonalny aspekt Lisp będą bronić tego stylu. Jednak niezależnie od preferencji, może być bardzo użyteczne, aby zawsze móc polegać na tym, że zmienne się nie zmieniają. Oto przykład, gdzie może to być ważne:

(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)) 

Następnie wartość zwracana FOO to funkcja, która po wywołaniu z nadrukiem wartość x. Ale wartość będzie równa x później w funkcji, wartość bezwzględna. Może to być bardzo zaskakujące, jeśli funkcja jest duża i skomplikowana.

+0

+1. Warto również zauważyć, że "(niech ((x (abs x))) ...)" również działa - nie tyle funkcja "abs", ale fakt, że ta sama nazwa zmiennej może być użyta do nowa zmienna. –

+3

Zamknięcie odnoszące się do zmodyfikowanej wartości przypomina mi jak w JavaScript, używanie pętli for do iteracji po tablicy przycisków i dołączanie instrukcji onclick, które odnoszą się do indeksu iteracyjnego, jest złym pomysłem –

+3

@LeCurious Zamknięcie-iteracja -zmienna kwestia pojawia się również w Common Lisp, szczególnie w [makro pętli] (http://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm). Na przykład zobacz [nieoczekiwane zachowanie z makropoleceniem pętli i zamknięciami] (http://stackoverflow.com/questions/15714537/unexpected-behavior-with-loop-mac-and-closures), które demonstruje to dokładne zachowanie. –

4

Nie wiem, Common Lisp wystarczająco dobrze, aby odpowiedzieć, jak to zrobić w Common Lisp, więc używam Scheme dla mojego przykładu poniżej. Załóżmy, że piszesz funkcję zwracającą silnię z liczby. Oto „modyfikować zmienne lokalne” podejście do tej funkcji (musisz zdefiniować własne while makro, ale nie jest to trudne):

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

Oto „ponownie powiązać zmienne lokalne” podejście do tej funkcji:

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

W tym przypadku wywoływana jest loop z nowymi wartościami do ponownego wiązania za każdym razem. Ta sama funkcja może być zapisana za pomocą do makro (który, nawiasem mówiąc, również używa ponownego wiązania, nie modyfikowanie, przynajmniej na schemacie):

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