Elisp 延长变量生命周期

Elisp extend variable lifetime

我有一个循环,我在每次迭代中创建一个局部变量。然后我声明一个 lambda,它在将来某个时候使用局部变量。

      (dolist (entry (read-lines "~/.emacs-projects"))
         (let ((project (car (json-read-from-string entry)))) ;; <---- I NEED THIS ONE
             (widget-create 'link
                     :button-prefix ""
                     :button-suffix ""
                     :action (lambda (wid &rest ignore) (load-project project)) ;; HERE
                     (format "%s : %s\n" (car project) (cdr project)))))

在上面的代码中,我创建了 project,当 :action 触发时,我想使用 project 作为另一个函数的参数。目前,当 lambda 为 运行 时,我得到 Symbol’s value as variable is void: project,这让我认为外部范围未保留。

如何延长 project 的生命周期以便我可以在 lambda 中访问它?

要么使用词法范围(非nil variable lexical-binding),要么使用列表 lambda 形式,用 project 的值代替变量本身:

 (dolist (entry (read-lines "~/.emacs-projects"))
         (let ((project (car (json-read-from-string entry)))) ;; <---- I NEED THIS ONE
             (widget-create 'link
                     :button-prefix ""
                     :button-suffix ""
                     :action `(lambda (wid &rest ignore) (load-project ',project)) ;; HERE
                     (format "%s : %s\n" (car project) (cdr project)))))

如果你的动作函数并不真的需要project作为变量,而只是需要它的值,那么你可以直接替换值,如图所示。

但在那种情况下,您的 lambda 形式将是一个列表(其汽车是 lambda,等等),因此在评估之前它不会被识别为函数。特别是,字节编译器只会将其视为一个列表,而不是一个函数。

您想使用 Emacs Lisp 的词法范围风格,这是添加到 Emacs-24 的主要创新之一。为此,只需在 Elisp 文件的第一行某处(通常在注释中)添加以下内容:

-*- lexical-binding:t -*-

希望在将来的某个时候,旧的动态范围方言将不再是默认方言。