3

我不明白 Emacs 24 的新词法作用域功能为何如此出色的原因是我想不出没有它们就无法实现的任何新功能。例如,下面的闭包:

(setq lexical-binding t)
(defun f1 (num1)
  (lambda (num2)
    (setq num1 (* num1 num2))))

(fset 'f2 (f1 5))
  ==> (closure ((num1 . 5) t) (num2) (setq num1 (* num1 num2)))
(f2 5)
  ==> 25
(f2 2)
  ==> 50

可以使用常规动态范围来实现,如下所示:

(defun f1 (num)
  (let ((tmpvar (make-symbol "num")))
    (set tmpvar num)
    `(lambda (num2)
       (set ',tmpvar (* (eval ,tmpvar) num2)))))

(fset 'f2 (f1 5))
  ==> (lambda (num2) (set (quote num) (+ (eval num) num2)))
(f2 5)
  ==> 25
(f2 2)
  ==> 50
(fset 'f3 (f1 9))
  ==> (lambda (num2) (set (quote num) (+ (eval num) num2)))
(f3 3)
  ==> 27
(f3 2)
  ==> 54
(f2 10)
  ==> 500

好的,所以并不是所有的语言都有类似于 elisp 的 uninterned 符号的东西,所以我理解为什么词法作用域在他们的情况下如此出色。但是 elisp 呢?由于词法作用域,你能想到我现在(从 Emacs 24 开始)我以前不能做的任何事情吗?

4

3 回答 3

5

您不需要非实习符号,使用cons代替make-symbolcar代替eval,和setcar代替,set您的示例也可以正常工作(并且更有效)。

另请注意,从机器语言到更高级别语言的发展主要是基于使越来越多的事情变得不可能(或至少更难)。当然,那些从程序员那里拿走的设施很少被使用和/或被认为太危险。考虑使用未初始化的变量(在 C 中可能,但在 Java 和许多其他语言中是不可能的),或者跳到指令的中间。

至于您的示例代码的一些缺点:不仅可读性较差,而且编译器基本上无法知道您正在构建代码,因此不允许查看“`(lambda .. .)" 来编译它,扩展它的宏调用,给你关于可疑元素的警告,......

于 2012-08-26T02:58:45.787 回答
4

在 Emacs 中一直有模拟词法绑定的变通方法,因此与其说是能够做新的事情。

手册说:

词法绑定为优化提供了更多机会,因此使用词法绑定的 Emacs Lisp 代码可能会在未来的 Emacs 版本中运行得更快。这样的代码对并发性也更加友好,我们希望在不久的将来将其添加到 Emacs 中。

我认为这是主要的好处。

与此相反的是,在编写 Emacs 时,出于充分的理由特意选择了动态绑定,这在今天仍然适用,因此词法绑定当然不应该被视为新的做事方式。

全局变量在编程中通常被认为是一个坏主意,但 Emacs 是一个不寻常的案例,它并不真正适用,因为它的大部分灵活性——使 Emacs 伟大的关键因素之一——直接来自动态绑定。如果没有动态绑定,就不可能像 Emacs 所允许的那样将应用程序弯曲到满足单个用户的要求。

在我看来,词法绑定应该谨慎使用,并且只用于其他用户无法找到理由重写的变量。默认情况下,变量应该是defvar'd 以便保留自定义行为的能力(即使以作者没有预料到的方式)。

于 2012-08-26T04:04:14.080 回答
0

我认为面向对象编程的实现往往很适合词法范围。具有状态映射的对象直接映射到词法闭包。

我确信 common lisp 中的 CLOS 实现大量利用了词法范围。我很难想象该规范如何仅在动态范围内实现,但我确信这是可能的。

于 2012-08-26T20:57:07.683 回答