理解 LISP 中的“let”表达式

Posted

技术标签:

【中文标题】理解 LISP 中的“let”表达式【英文标题】:Understanding "let" expression in LISP 【发布时间】:2015-07-26 14:55:01 【问题描述】:

我对 lisp 非常陌生,之前有过函数式编程(Haskell,SML)的经验。为什么这段代码返回14,而不是10(即1 + 2y + 3 + 1)?

(defvar x 1)

(defun g (z)
  (+ x z))

(defun f (y)
  (+ (g 1)
     (let ((x (+ y 3)))
        (g (+ y x)))))

(f 2)

【问题讨论】:

【参考方案1】:

原因是您使用的是带有 dynamic binding 的 Lisp 方言(Emacs Lisp 文档中对此有很好描述的链接)。

详细地说,您的程序的行为方式是因为当从 let 表达式中调用 g 时,由 let 表达式创建的 x 的新绑定取代了 (defvar x 1) .因此,g 函数不是将 1 添加到其参数,而是将 x 的当前值添加到 let 表达式中时为 5。

【讨论】:

你怎么知道这是elisp?这也是有效的 common-lisp。 @Sylwester:我不知道它是 elisp,但这是我发现的第一个文档页面,它简洁地解释了动态绑定。【参考方案2】:

因为您使用了(DEFVAR X 1),它将X 声明为全局特殊变量。然后,这会导致 X 的所有其他后续绑定使用动态绑定:在 (LET ((X ... 中。

Lisp 中的样式和约定

Lisp 中的约定:对特殊变量使用 *X* 而不是 X

(defvar *x* 1)

那么你的代码是:

(defvar *x* 1)   ; global special variable *X*

(defun g (z)
  (+ *x* z))     ; use special variable *X*

(defun f (y)
  (+ (g 1)
     (let ((x (+ y 3)))    ; lexical binding of X
        (g (+ y x)))))     ; use lexical binding of X

运行:

? (f 2)
10

【讨论】:

以上是关于理解 LISP 中的“let”表达式的主要内容,如果未能解决你的问题,请参考以下文章

Common Lisp宏中的词法绑定

在 emacs lisp 中使用 let 和 flet

Lisp 的 let* 的 Ocaml 等价物?

Lisp 简单问题

有没有办法在 Lisp 中声明局部变量(避免 let)?

写一个简单的lisp解释器