2016-07-26 29 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))))