2014-07-17 107 views
1

我想用Emacs Lisp定義一個累加器列表並且寫下面的代碼,但是我得到一個錯誤,說initV是一個無效變量。看起來initV未在功能define-accum中評估。我在哪裏犯了一個錯誤? (我只想知道爲什麼,但我知道有其他方法可以達到我的目標。)爲什麼不評估Emacs lisp函數的參數?

(defun define-accum (name initV) 
    (defalias name (lambda (v) (+ v initV)))) 

(setq accums '((myadd1 . 1) 
       (myadd2 . 2))) 

(dolist (a accums) 
    (define-accum (car a) (cdr a))) 

(message "result = %d" (+ (myadd1 1) (myadd2 1))) 

回答

3
使用反引號

除了見here,你可以activate lexical binding(如果你使用Emacs 24或更高版本)。例如,如果我把你的代碼在一個.el文件,並把這個第一行:

;; -*- lexical-binding: t -*- 

然後我得到的輸出:

result = 5 

這工作,因爲在define-accum lambda函數將引用initV在被定義的環境中(因此在參數列表中選擇變量),並在該變量上創建一個閉包。通過動態綁定(默認),該函數將在調用它的環境中查找initV

3

您需要正確使用反引號。這會爲你工作,例如:

(defun define-accum (name initV) 
    (defalias name `(lambda (v) (+ v ,initV)))) 

一個解釋

+0

感謝。我知道但是我仍然不明白爲什麼應該引用 –

+2

問題是'lambda'是自引用的,因此'initV'確實在別名中,它在評估別名時是未定義的。 – Tobias

1

要一點點添加到其他人所說 -

  1. 如果變量(initV)從未實際使用作爲變量,使當時的蓄電池實際上它的被定義是所有需要的,那麼不需要封裝該變量及其值的詞法閉包。在這種情況下,@juanleon描述的方法就足夠了:它只使用定義時的值 - 當調用函數時(如您發現的),該變量不存在。

  2. 另一方面,詞法閉包方法允許函數進行字節編譯。在反引用方法中,該函數在運行時僅由表示列表表示,該列表表示lambda表單。如果lambda表單代表代價昂貴的代碼,那麼使用詞法閉包方法是有意義的,儘管(在這種情況下)變量並不是真正需要的(作爲變量)。

  3. 但是你可以明確地字節編譯功能(例如,##名##在define-accum。這將需要在#2中提到的低效的照顧,上面。

+0

請注意,閉包不再像反引號lambdas一樣喜歡「變量」,唯一的區別是,字節編譯器會在需要時爲你重寫代碼(例如,它會轉向'(let((a 1 ))...(setq a ..)... a ...)'into'(let((a(list 1)))...(setcar a ..)...(car a)。 ..)')。 – Stefan

相關問題