我试图理解以下两段代码:
(defun make-adder1 (n) `(lambda (x) (+ ,n x)))
(defun make-adder2 (n) (lexical-let ((n n)) (lambda (x) (+ n x))))
这两者似乎都产生了咔嗒声:
(funcall (make-adder1 3) 5) ;; returns 8
(funcall (make-adder2 3) 5) ;; returns 8
这两个都有效。我有两个主要问题:
1)我不理解"引用级别"这两种方法之间。在第一种情况下,引用lambda表达式,这意味着"符号本身"返回而不是值。在第二种情况下,似乎将对lambda的语句进行求值,因此将返回lambda的值。然而,这些都与funcall
一起使用。在funcall
' ed函数上使用defun
时,必须引用它。词汇 - 让我自动做某种引用吗?不是吗,有点令人惊讶吗?
2)阅读关于这个主题的其他帖子,我了解到第一种方法在某些情况下会崩溃,并且偏离了人们对使用lambda和其他语言中的高阶函数的期望,因为elisp默认具有动态范围。有人能给出一个具体的代码示例,使这种差异显而易见并解释它吗?
答案 0 :(得分:2)
在第一个示例中,结果函数中没有变量n
,只有(lambda (x) (+ 3 x))
。它不需要词法绑定,因为lambda中没有自由变量,即没有需要保存在闭包绑定中的变量。如果您不需要变量n
可用,作为变量使用该函数,即,如果它在函数定义时间(= 3)的值是您需要的全部内容,那么第一个例子就是你所需要的。
(fset 'ad1 (make-adder1 3))
(symbol-function 'ad1)
返回:
(lambda (x) (+ 3 x))
第二个例子创建了一个实际上是创建和应用复杂闭包的函数。
(fset 'ad2 (make-adder2 3))
(symbol-function 'ad2)
返回
(lambda (&rest --cl-rest--)
(apply (quote (closure ((--cl-n-- . --n--) (n . 3) t)
(G69710 x)
(+ (symbol-value G69710) x)))
(quote --n--)
--cl-rest--))
第三种选择是使用lexical-binding
文件局部变量并使用最直接的定义。这会创建一个简单的闭包。
;;; foo.el --- toto -*- lexical-binding: t -*-
(defun make-adder3 (n) (lambda (x) (+ n x)))
(fset 'ad3 (make-adder3 3))
(symbol-function 'ad3)
返回:
(closure ((n . 3) t) (x) (+ n x))
(symbol-function 'make-adder1)
返回:
(lambda (n)
(list (quote lambda)
(quote (x))
(cons (quote +) (cons n (quote (x))))))
(symbol-function 'make-adder2)
返回:
(closure (t)
(n)
(let ((--cl-n-- (make-symbol "--n--")))
(let* ((v --cl-n--)) (set v n))
(list (quote lambda)
(quote (&rest --cl-rest--))
(list (quote apply)
(list (quote quote)
(function
(lambda (G69709 x)
(+ (symbol-value G69709) x))))
(list (quote quote) --cl-n--)
(quote --cl-rest--)))))
(symbol-function 'make-adder3)
返回
(closure (t) (n) (function (lambda (x) (+ n x))))