使用elisp局部变量而不是全局变量将函数添加到模式挂钩中

时间:2015-10-01 02:47:52

标签: emacs elisp let

所以我试图在某些elisp模式钩子中添加一些东西 - 具体来说,我想定义一个钩子prettify-symbols-alist,然后通过调用prettify-symbols-mode来专门激活它。

在任何情况下,我都会org-babel将值导出到表中的一对列表中,使用pairlis将它们绑定在一起作为列表,add-hook它使用匿名函数进入所需模式。

所以,问题是,现在如果我使用全局变量,如下所示,它可以工作:

(let ((token (quote ("not" "*" "/" "->" "map" "/=" "<=" ">=" "lambda")))
      (code (quote (172 215 247 8594 8614 8800 8804 8805 955)))) ; Generated automatically using org-babel

  (require 'cl)

  (setq *globalvar (pairlis token code))

  (add-hook 'emacs-lisp-mode-hook
            (lambda ()
               (setq prettify-symbols-alist *globalvar)
               (prettify-symbols-mode 1))))

但是,如果我尝试不使用全局变量,通过这种方式,它不起作用:

(let ((token (quote ("not" "*" "/" "->" "map" "/=" "<=" ">=" "lambda")))
      (code (quote (172 215 247 8594 8614 8800 8804 8805 955)))) ; Generated automatically using org-babel
  (let (localv)
    (require 'cl)

    (setq localv (pairlis token code))

    (add-hook 'emacs-lisp-mode-hook
              (lambda ()
                 (setq prettify-symbols-alist localv)
                 (prettify-symbols-mode 1))))

我知道为什么:如果我C-h v emacs-lisp-mode-hook,我会看到它引用我在let形式中使用的任何变量,该变量在变量存在时起作用,如{{1}但是,当我使用*globalvar时,它不再存在于localvar形式之外。但我不确定如何强制评估局部变量本身,因为我仍然在努力研究elisp中的许多概念,这些概念对我来说并不是很清楚。

我错过了什么?我在哪里错了?

3 个答案:

答案 0 :(得分:2)

首先将lexical-binding设置为非nil,否则localv将成为钩子函数中的自由变量。最好将lexical-binding设置为文件局部变量。

此外,您的代码中没有任何内容可以使localv 缓冲区本地。大概你想给它一个在该模式下缓冲区本地的值。绑定它的代码应该在相关的模式(即缓冲区)中进行评估。

答案 1 :(得分:1)

好的,这就是我最后所做的:

(let ((token (quote ("not" "*" "/" "->" "map" "/=" "<=" ">=" "lambda")))
      (code (quote (172 215 247 8594 8614 8800 8804 8805 955))))
  (require 'cl)

  (lexical-let (localv)
    (setq localv (pairlis token code))
    (add-hook 'emacs-lisp-mode-hook
               (lambda ()
                  (setq prettify-symbols-alist localv)
                  (prettify-symbols-mode 1)))))

我最终使用phils'建议使用lexical-let而不是Drew的建议,主要是因为我正在使用org-babel将代码块纠缠到我的源代码中代码(基本上我使用org-mode来组织我的设置文件),似乎没有办法设置lexical-binding文件局部变量 - 根据this页面,你需要将它设置为第一行(使用;; -*- lexical-binding: t -*-),我还找不到办法。

无论如何,感谢所有帮助我解决这个问题的人!

答案 2 :(得分:1)

我喜欢这个解决方案。使用lexical-let,对lambda(内部add-hook)的调用会生成一个闭包,因为您可以看到键入M-x ielm RET和{{1检查它的价值。

你也可以使用这样的老式风格反击:

emacs-lisp-mode-hook RET

(编辑)

请注意(add-hook 'emacs-lisp-mode-hook `(lambda () (setq prettify-symbols-alist ',localv) (prettify-symbols-mode 1))) 之前的backticklambda之前的quote-comma(如tarikq所述)。

我认为你有意义!

实际上,不是“展开它然后引用它”,我会说“插入它的值(来自词汇环境),然后引用它”。

如果你尝试这样的话:

localv

然后你会得到lisp在运行时实际会做的事情:

 (macroexpand '`(lambda ()
                  (setq prettify-symbols-alist ',localv)
                  (prettify-symbols-mode 1)))

您将看到如何构建整个列表,以及 (cons 'lambda (cons nil (cons (list 'setq 'prettify-symbols-alist (list 'quote localv)) '((prettify-symbols-mode 1))))) 通常评估的方式,即未引用(与符号localv和{{1}进行比较}和列表'setq