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)))

ほとんどの言語では、下のフォームからainsome-funcが見えないように見えるため、これは 3 を返します。ただし、24 より前の emacs またはレキシカル スコープのない 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

説明:

現在の動的環境とヌル字句環境でフォーム を評価します。

説明を念頭に置いて、例を 1 つずつ見てみましょう。

(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

nilに設定することでレキシカルバインディングが無効になるlexical-bindingためmyvar、動的にバインドされた変数になり、内部eval関数内で使用できます。let内部関数を含むフォームは、外部関数が呼び出されるeval前に引数の準備として評価されることに注意してください。evalここでは、外部関数のオプションも内部eval関数のオプションも関係ありません。

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

myvarr字句的にバインドされた変数になりますが、これは inner 内では使用できませんeval'のため、フォームは字句バインディングが有効なlet外部関数によって評価されることに注意してください。evalここでは外側のeval関数のオプションが関係しますが、内側の関数のオプションは関係evalありません。


null レキシカル環境はなぜですか?

つまり、現在の字句環境が使用された場合、そのアルファ等価性はもはや保持されないためだと思います。

アルファ等価性は、関数パラメーターの名前が重要ではないことを示す正式な方法です。たとえば、(lambda (x) x)(lambda (y) y)はアルファ等価であり、同じものと見なしてきました。アルファ等価性により、いつでも必要に応じて関数パラメーターの名前を変更できます。私たちはそれを当然のことと思っています。より正式な説明については、ラムダ計算アルファ等価を参照してください。

しかし、自由変数 (オープンコードと呼ばれる) を含むコード値を Lisp のように渡すことができる場合、アルファ等価性にはいくつかの問題があることがわかります。次の例を見てみましょう。

;;; -*- 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)ます。結果の関数値が同じ引数に適用されるとどうなるかを考えてみてください。ポイントは、自由変数を含むコード値が許可されている場合、関数パラメーターの名前が問題になるということです。

ここで、アルファ等価性を維持するために 2 つの選択肢があります。アルファ等価性を放棄することはできません。なぜなら、アルファ等価性は非常に自然に感じられ、慣れているからです。最初のオプションはeval、(Common) Lisp が行うように、null 字句環境で評価することです。このオプションを使用すると、 in の自由変数yは inの仮パラメーターに(1+ y)バインドされません。したがって、これら 2 つのプログラムは一貫して動作し、アルファ等価性が適切に維持されます。もう 1 つの選択肢は、オープン コード値を最初から許可しないことで問題を排除することです。MetaOCamlが選んだ方法だと聞いたことがありますy(lambda (y) ...)

「もしそうなら、なぜ現在の動的環境なのですか?」と尋ねる人がいるかもしれません。動的にバインドされた (別名、特殊な) 変数の場合、それらの名前が非常に重要であり、不注意に変更するとプログラムが壊れてしまうことは既に十分に認識されています。

于 2012-08-20T00:15:18.070 に答える
2

あなたの内部フォームの奇妙な動作は、字句バインディングまたはその下の動作にeval似ているようです。symbol-valueadd-to-list

emacs 字句スコープと引用符で囲まれた変数

  • 字句バインディングの下で​​は、シンボルの値セルはグローバル値のみを保持します (変数の字句バインディング値とは異なる場合があります)。

  • 引用符で囲まれた変数 ( (eval 'var)(eval '(print var))、など(add-to-list 'var 1)) の使用は、シンボルの値セルでのみ取得または設定されます。

この種の落とし穴に遭遇しないようにするためのプラクティスは次のとおりです。

  • 可能な限り、eval ではなくマクロを定義して使用するようにしてください。

  • グローバル変数を作成/宣言する場合は、 を使用setqして作成することは控え、代わりにdefvardefcustomまたはを使用して特別な変数として作成してください。defconst特殊変数は、レキシカル バインディング モードであっても、常に動的にスコープされます。

于 2012-08-19T05:55:16.517 に答える