5

2013 年 5 月更新:从 GNU Emacs 24.3.1 开始,(let .. (defun..)) bytecompiles 很好,没有警告,bytecompiled 代码与未编译代码一样工作。只是不要忘记将文件变量添加lexical-binding: t到要进行字节编译的文件中。现在不需要在此问题末尾的解决方法。


Lexical Binding - Emacs Lisp Manual有这一段:

请注意,symbol-value、boundp 和 set 等函数仅检索或修改变量的动态绑定(即其符号值单元格的内容)。此外,defun 或 defmacro 主体中的代码不能引用周围的词法变量。

我不确定我是否理解了第二句话的意思。在下面应该以词法绑定模式运行的代码中,defun 主体中的代码成功地引用了 name 的词法绑定值n

(let ((n 0))
  (defun my-counter ()
    (incf n)))

(my-counter) ;; 1
(my-counter) ;; 2

这句话只是说 (let .. (defun ..)) 是一种不好的做法吗?


解决方法

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

;; a way to define the counter function without byte-compile error or warning

(defvar my--counter-func
  (let ((n 0))
    (lambda ()
      (setq n (1+ n)))))

(defun my-counter ()
  (funcall my--counter-func))

;; another way to define the counter function, again without byte-compile error or warning

(fset 'my-another-counter
      (let ((n 0))
        (lambda ()
          (setq n (1+ n)))))

这是测试上述代码的代码:

;; run:
;; emacs -q --load path-to-the-el-file-of-this-code.el

(load "path-to-file-defining-my-counter.elc") ;; loading the ELC file to test if byte-compiled code runs as expected.

(print (my-counter)) ;; 1
(print (my-counter)) ;; 2

(print (my-another-counter)) ;; 1
(print (my-another-counter)) ;; 2
4

3 回答 3

1

至少在 Emacs 24.1.1 中,代码不能很好地进行字节编译。我在文件中保存了以下代码foo.el,它使用setq代替incf以避免cl库可能产生的任何影响:

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

(let ((n 0))
  (defun my-counter ()
    (setq n (1+ n))))

当我尝试对其进行字节编译(M-x byte-compile-filefoo.el)时,我收到以下警告消息:

foo.el:3:1:Warning: Function my-counter will ignore its context (n)
foo.el:3:1:Warning: Unused lexical variable `n'
foo.el:5:11:Warning: reference to free variable `n'
foo.el:5:17:Warning: assignment to free variable `n'

所有消息都表明defun构造体中的代码不能n像手册所声称的那样引用周围的词法变量。

实际上,当我加载字节编译代码 ( M-x load-filefoo.elc) 并(my-counter)评估表单时,我得到了以下错误:

Debugger entered--Lisp error: (void-variable n)
  ...

不幸的是,我不确定为什么在以源代码形式评估时代码似乎可以工作。

于 2012-08-20T06:01:57.110 回答
1

正如我在 gnu.emacs.help 上回复的那样,您可以使用 (defalias 'foo (lambda ...)) 来解决该限制。在 Emacs 的开发代码中,这个限制被解除了。

于 2012-08-22T15:38:57.140 回答
0

就像您在上面所做的那样,从一个定义中引用词法范围(*)中的变量是非常好的,就像该手册页上的“my-ticker”示例一样。

要么我错过了手册中说的那一行:

defun 或 defmacro 主体中的代码不能引用周围的词法变量。

应该说更像:

defun 主体中的代码只能访问在相同词法范围内定义的词法变量。

注意:其他答案中有关于字节编译此类代码的问题的评论。这些应该在最新的 emacs 中修复。我已经在 v24.2.50.1 中验证了这个字节可以正确编译和加载。

于 2012-08-20T04:29:57.023 回答