我想用Emacs Lisp定义一个累加器列表并编写以下代码,但是我得到一个错误,说initV
是一个void变量。似乎initV
未在函数define-accum
中进行评估。我哪里弄错了? (我只是想知道为什么,虽然我知道还有其他方法可以实现我的目标。)
(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)))
答案 0 :(得分:3)
您需要正确使用反引号。这对你有用,例如:
(defun define-accum (name initV)
(defalias name `(lambda (v) (+ v ,initV))))
请参阅here了解
答案 1 :(得分:3)
除了使用反引号,您可以activate lexical binding(如果您使用的是Emacs 24或更新版本)。例如,如果我将您的代码放在.el
文件中并将其放在第一行:
;; -*- lexical-binding: t -*-
然后我得到输出:
result = 5
这是有效的,因为define-accum
中的lambda函数将在定义它的环境中引用initV
(从而在参数列表中选择变量),并在此变量上创建闭包。使用动态绑定(默认),该函数将在调用它的环境中查找initV
。
答案 2 :(得分:1)
添加一些其他人所说的内容 -
如果变量(initV
)从未实际使用作为变量,那么实际上它在累加器时的值定义是所有需要的,然后不需要封装该变量及其值的词法闭包。在这种情况下,@ juanleon描述的方法就足够了:它只使用定义时的值 - 当调用函数时,变量不存在(如你所发现的那样)。
另一方面,词法闭包方法允许函数进行字节编译。在反引号方法中,函数仅在运行时由表示lambda表单的 list 表示。如果lambda表单代表昂贵的代码,那么使用词法闭包方法是有意义的,即使(在这种情况下)变量不是真正需要的(作为变量)。
但是你总是可以明确地对该函数进行字节编译(例如define-accum
中的## NAME ##。这将解决上面#2中提到的低效率。