4

谁能解释一下evalemacs24是如何工作的?从eval描述:

eval is a built-in function in `C source code'.

(eval FORM &optional LEXICAL)

Evaluate FORM and return its value.
If LEXICAL is t, evaluate using lexical scoping.

这是否意味着,这样的事情应该有效?

(setq lexical-binding t)
(let ((myvarr 42)) (eval 'myvarr t)) ; (void-variable myvarr)

更新:

(setq lexical-binding nil)
;; => nil
(let ((myvarr 42)) (eval 'myvarr))
;; => 42 (#o52, #x2a, ?*)
(setq lexical-binding t)
;; => t
(let ((myvarr 42)) (eval 'myvarr))
;; Debugger entered--Lisp error: (void-variable myvarr)
;;   eval(myvarr)
;;   (let ((myvarr 42)) (eval (quote myvarr)))
;;   (progn (let ((myvarr 42)) (eval (quote myvarr))))
;;   eval((progn (let ((myvarr 42)) (eval (quote myvarr)))) t)
;;   eval-last-sexp-1((4))
;;   eval-last-sexp((4))
;;   call-interactively(eval-last-sexp nil nil)
;;   call-last-kbd-macro(nil kmacro-loop-setup-function)
;;   kmacro-call-macro(nil nil)
;;   kmacro-end-or-call-macro(nil)
;;   call-interactively(kmacro-end-or-call-macro nil nil)
(ignore-errors (let ((myvarr 42)) (eval 'myvarr)))
;; => nil
(setq lexical-binding nil)
;; => nil
(eval (let ((myvarr 42)) (eval 'myvarr)) t)
;; => 42
(eval '(let ((myvarr 42)) (eval 'myvarr)) t)
;; Debugger entered--Lisp error: (void-variable myvarr)
;;   eval(myvarr)
;;   (let ((myvarr 42)) (eval (quote myvarr)))
;;   eval((let ((myvarr 42)) (eval (quote myvarr))) t)
;;   eval((eval (quote (let ((myvarr 42)) (eval (quote myvarr)))) t) nil)
;;   eval-last-sexp-1((4))
;;   eval-last-sexp((4))
;;   call-interactively(eval-last-sexp nil nil)

Emacs 版本:GNU Emacs 24.1.1 (i386-mingw-nt6.1.7600) of 2012-06-10 on MARVIN

4

3 回答 3

8

由于词法绑定破坏了许多现有的 elisp 代码,因此它是一个可选功能。

用一个简单的例子可以最好地理解词法作用域:

(defun some-func (callback)
 (let ((a 5))
  (funcall callback)))

(let ((a 3))
 (some-func (lambda () a)))

在大多数语言下,这将返回 3,因为从底部表单中似乎看不到ain 。some-func但是,在 24 之前的 emacs 或没有词法范围的情况下,此程序返回 5。

这导致了许多意想不到的惊喜以及交互功能之间经常隐藏的微妙错误;为了解决这个问题,emacs 24 引入了词法作用域,但如前所述,它是向后兼容的。

选择加入动态范围的机制是文件变量lexical-binding(每个源文件都打开它)或作为您看到的选项eval

因此,如果我们重写示例以使用eval

(eval '(let ((a 3))
        (some-func (lambda () a))) nil) ; => 5

(eval '(let ((a 3))
        (some-func (lambda () a))) t) ; => 3

在您的示例中,不同之处不是内部代码是否eval是动态范围的,而是它周围的代码是否是。变量绑定受词法作用域影响,而不是变量查找。我不完全确定eval' 在这里的语义,但似乎正在发生的事情(以及最有意义的事情)是eval在全新的词汇上下文中评估表达式。所以外部词法范围从 内部隐藏eval,但动态范围仍然可见(因此当文件是动态范围时查找成功,但不是其他情况)。

于 2012-06-15T19:03:56.647 回答
5

我在 Emacs 24 中找不到该eval函数的形式语义,但假设它的工作方式与 Common Lisp 中的相同(如 cobbal 所示),您的所有示例都是有意义的。Common Lisp HyperSpec说:

句法:

eval 形式

描述:

在当前动态环境和词法环境中评估形式

让我们在记住描述的情况下一个一个地看你的例子。

(setq lexical-binding t)
(let ((myvarr 42)) (eval 'myvarr t)) ; Lisp error: (void-variable myvarr)

词法绑定通过设置t来启用lexical-binding,因此myvarr变成了词法绑定变量,eval如上所述,该变量在函数内部不可用。eval函数的选项 ,在t这里无关紧要。

(setq lexical-binding nil)
(let ((myvarr 42)) (eval 'myvarr)) ; 42

nil设置为禁用词法绑定lexical-binding,因此myvarr变为动态绑定变量,可在eval函数内部使用。函数的eval选项 impliciltynil在这里无关紧要。

(setq lexical-binding t)
(let ((myvarr 42)) (eval 'myvarr)) ; Lisp error: (void-variable myvarr)

词法绑定通过设置t来启用lexical-binding,所以myvarr变成了词法绑定变量,在eval函数内部是不可用的。函数的eval选项 impliciltynil在这里无关紧要。

(ignore-errors (let ((myvarr 42)) (eval 'myvarr))) ; nil

同上。

(setq lexical-binding nil)
(eval (let ((myvarr 42)) (eval 'myvarr)) t) ; 42

通过设置为 禁用词法绑定nillexical-binding因此myvar变为动态绑定变量,可在内部eval函数中使用。请注意let,包括内部eval函数在内的表单在调用外部函数之前被评估为参数准备eval。外部函数和内部eval函数的选项在这里都不相关。

(eval '(let ((myvarr 42)) (eval 'myvarr)) t) ; Lisp error: (void-variable myvarr)

myvarr变成了一个词法绑定的变量,它在内部是不可用的eval。请注意,由于',表单由启用词法绑定let的外部函数评估。eval外部eval函数的选项在这里是相关的,而内部eval函数的不是。


为什么是空词法环境?

也就是说,我认为,因为如果使用当前的词汇环境,这种 alpha 等价性将不再成立。

Alpha-equivalence 是一种正式的方式来表示函数参数的名称并不重要。例如,(lambda (x) x)(lambda (y) y)是 alpha 等价的,我们认为它们是相同的。Alpha 等效性允许我们随时更改函数参数的名称。我们认为它是理所当然的,如果它不成立,我们会感到非常惊讶。有关更正式的解释,请参阅Lambda 演算Alpha-equivalence

但是当涉及自由变量的代码值(称为开放代码)可以像在 Lisp 中一样被传递时,alpha 等效性就会出现一些问题。让我们看看下面的例子:

;;; -*- lexical-binding: t -*-
(lambda (x) (lambda (y) (eval x))) '(1+ y)

如果eval在当前的词法环境下求值,形式将等价于(lambda (y) (1+ y)). 现在看下面的程序:

(lambda (x) (lambda (z) (eval x))) '(1+ y)

这个程序与前一个程序的不同之处仅在于它的参数名称,z而不是y,因此我们自然希望它们以相同的方式运行。但后一个程序的计算结果为(lambda (z) (1+ y)),这与 绝对不同(lambda (y) (1+ y)。考虑将结果函数值应用于同一参数时会发生什么。关键是当允许包含自由变量的代码值时,函数参数的名称确实很重要。

在这里,为了保持 alpha 等效性,我们有两个选择:我们不能放弃 alpha 等效性,因为它感觉很自然,而且我们已经非常习惯了。第一个选项是eval像(Common)Lisp 那样在空词法环境下进行评估。使用此选项,自由变量yin(1+ y)不会绑定到 in 的形y(lambda (y) ...)。因此,这两个程序的行为一致,并且 alpha 等效性得到了很好的保留。另一种选择是通过从一开始就不允许开放代码值来排除问题。听说是MetaOCaml选择的方法

Someone may ask "if so, why the current dynamic environment?". For dynamically bound (a.k.a. special) variables, we already acknowledge well that their names are so important and that, if you change them carelessly, your program will be broken.

于 2012-08-20T00:15:18.070 回答
2

您内部形式的奇怪行为eval似乎类似于symbol-valueadd-to-list在词法绑定下。

emacs 词法作用域和引用变量

  • 在词法绑定下,符号的值单元格仅保存全局值(可以不同于变量的词法绑定值)。

  • 使用带引号的变量(例如 in (eval 'var)(eval '(print var))(add-to-list 'var 1))只能在符号的值单元格上获取或设置。

避免遇到这种问题的做法是:

  • 尽可能尝试定义和使用宏而不是 eval

  • 如果您想创建/声明一个全局变量,请避免使用setq来创建它,使用defvardefcustomdefconst其创建为特殊变量。特殊变量始终是动态范围的,即使在词法绑定模式下也是如此。

于 2012-08-19T05:55:16.517 回答