来自Google Common Lisp样式指南:“避免修改局部变量,尝试重新绑定”意思是什么?

时间:2013-06-18 08:07:45

标签: lisp common-lisp

Google Common Lisp风格指南说Avoid modifying local variables, try rebinding instead

这是什么意思?重新绑定在那句话中意味着什么?

2 个答案:

答案 0 :(得分:9)

这意味着您应该创建新变量而不是更改旧变量的值。例如,让我们采用以下代码:

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

相反,应该创建一个新的绑定并使用该绑定:

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

这样做的原因是你总是知道变量包含什么,因为它永远不会改变。如果您想要新值,只需使用包含该新值的变量。

现在您可能会问为什么这么重要?嗯,有些人比其他人更喜欢这个。特别是那些喜欢强调Lisp功能方面的人会提倡这种风格。但是,无论偏好如何,始终能够依赖变量不会改变的事实是非常有用的。以下是一个重要的例子:

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

然后,FOO的返回值是一个函数,当使用print调用x的值时。但是,该值将是函数后面x的值,即绝对值。如果功能大而复杂,这可能会非常令人惊讶。

答案 1 :(得分:4)

我不太清楚Common Lisp如何在Common Lisp中做到这一点,所以我在下面的示例中使用Scheme。假设您正在编写一个函数来返回数字的阶乘。这是对该函数的“修改局部变量”方法(您必须定义自己的while宏,但这并不难):

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

这是该函数的“重新绑定局部变量”方法:

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

在这种情况下,每次调用loop时都会使用新值重新绑定。可以使用do宏来编写相同的函数(顺便说一句,它也使用重新绑定,而不是修改,至少在Scheme中):

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