2016-07-26 42 views
1

我试图理解的代码下面的两个片段:elisp的lambda表达式,报价,和词汇,让

(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。在defun编辑功能上使用funcall时,必须引用它。是词汇 - 让我们自动做一些引用?这是不是有点令人惊讶?

2)阅读关于这个主题的其他文章,我明白,在某些情况下,第一种方法会崩溃,并偏离用lambdas和其他语言中的高阶函数处理期望的结果,因为elisp默认情况下具有动态范围。有人可以给出一个具体的代码示例,使这种差异明显并解释它吗?

+1

这必须是重复的,但我没有时间去寻找它... – Drew

回答

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