如何解决emacs lisp闭包中的局部变量不足问题

11
我现在正在学习Emacs Lisp,参考手册是reference manual,同时也在从一本LISP书中学习Common Lisp。
来自Common Lisp书籍。
>> (setf power-of-two
     (let ((previous-power-of-two 1))
       #'(lambda ()
           (setf previous-power-of-two
             (* previous-power-of-two 2)))))

>> (funcall power-of-two)
2

>> (funcall power-of-two)
4

>> (funcall power-of-two)
8

由于Emacs Lisp的动态绑定行为,该函数无法正常工作。

我想知道是否有可能在不引入全局变量的情况下在Emacs Lisp中实现同样的功能?


你可以在emacs中使用Common Lisp和Slime来节省很多时间。SBCL是我的最爱。 - justinhj
@justinhj:谢谢你的建议。不过,我学习Lisp只是为了更好地了解Emacs。除非有什么能改变我的想法,否则我认为我更喜欢Python而不是Lisp。 - Sake
这很有道理。顺便说一下,当使用Emacs Lisp扩展Emacs时,请考虑如何在Python中执行相同的操作。在我看来,Emacs Lisp是扩展文本编辑器的理想语言,而Python则有许多不同的用途,我也非常喜欢它。 - justinhj
Emacs Lisp并不能真正反映出像Common Lisp这样针对“真正编程”的Lisp的使用体验。因此,与Python进行公平比较并不合适,因为Python并非首选用作嵌入式脚本语言。 - Marcin
Marcin,我相信Lisp是一个非常强大的概念。不幸的是,即使过了几个月,我个人仍然不太熟悉Lisp语法。 - Sake
4个回答

18

更新:

目前,Emacs 24已正式发布,当缓冲区本地变量lexical-binding非空时,它支持使用词法绑定而不需要使用lexical-let。请参见M-:(info“(elisp)using lexical binding”)和Pokita的答案。


您可以从Common Lisp Extensions(“CL包”)中使用lexical-let

elisp> (require 'cl)
cl
elisp> (setf power-of-two
             (lexical-let ((previous-power-of-two 1))
               #'(lambda ()
                   (setf previous-power-of-two
                         (* previous-power-of-two 2)))))
(lambda
  (&rest --cl-rest--)
  (apply
   (lambda
     (G175638)
     (set G175638
          (*
           (symbol-value G175638)
           2)))
   '--previous-power-of-two-- --cl-rest--))

elisp> (funcall power-of-two)
2
elisp> (funcall power-of-two)
4
elisp> (funcall power-of-two)
8

我也听说过 GNU Emacs 的 lexbind 分支。


使用 M-x ielm 命令可以打开 elisp REPL。 - phils
@phil 我猜这个提示是给我的吧? :) - Sake
这是一篇很好的阅读材料,可以深入了解Emacs Lisp中的词法作用域。http://www.gnu.org/software/emacs/emacs-paper.html#SEC17 - justinhj

12

现在从bzr获取的Emacs24已经原生支持词法绑定,但由于许多包仍然故意或无意中依赖动态作用域,因此默认情况下未激活。只要在将变量'lexical-binding'设置为't'的缓冲区中,您上面的代码应该能够正常工作。


1
感谢提供的信息。我非常好奇是否可能实现从动态绑定到词法绑定的无痛转换。但很高兴知道Emacs仍在不断发展。当我的技能足够好时,我真的很期待投入一些努力来帮助。 - Sake
动态绑定是Emacs如此易于扩展的关键原因之一。当然,有备选方案是很好的,但我希望动态绑定将始终成为大多数elisp代码的标准。 - phils

2

1

使用Emacs的unintern符号的另一种解决方案:

ELISP> (setf power-of-two
         (let ((p (make-symbol "previous-power-of-two")))
           (set p 1) (list 'lambda '()
           (list 'setf p
             (list '* p 2)))))

ELISP> (funcall power-of-two)
2
ELISP> (funcall power-of-two)
4
ELISP> (funcall power-of-two)
8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接