5

2013 年 5 月の更新: GNU Emacs 24.3.1 の時点で、(let .. (defun..)) は警告なしで問題なくバイトコンパイルされ、バイトコンパイルされたコードはコンパイルされていないコードと同じように機能します。バイトコンパイルするファイルにfile 変数を追加することを忘れないでくださいlexical-binding: t。この質問の最後にある回避策は不要になりました。


レキシカル バインディング - Emacs Lisp マニュアルには次の段落があります。

symbol-value、boundp、set などの関数は、変数の動的バインディング (つまり、そのシンボルの値セルの内容) のみを取得または変更することに注意してください。また、defun または defmacro の本体のコードは、周囲のレキシカル変数を参照できません。

2 番目の文の意味を正しく理解しているかどうかはわかりません。レキシカル バインディング モードで実行する必要がある次のコードでは、defun の本体のコードが name のレキシカル バインディング値を正常に参照していますn

(let ((n 0))
  (defun my-counter ()
    (incf n)))

(my-counter) ;; 1
(my-counter) ;; 2

(let .. (defun ..)) は悪い習慣だと言っているだけですか?


回避策:

;; -*- lexical-binding: t -*-

;; a way to define the counter function without byte-compile error or warning

(defvar my--counter-func
  (let ((n 0))
    (lambda ()
      (setq n (1+ n)))))

(defun my-counter ()
  (funcall my--counter-func))

;; another way to define the counter function, again without byte-compile error or warning

(fset 'my-another-counter
      (let ((n 0))
        (lambda ()
          (setq n (1+ n)))))

上記のコードをテストするためのコードは次のとおりです。

;; run:
;; emacs -q --load path-to-the-el-file-of-this-code.el

(load "path-to-file-defining-my-counter.elc") ;; loading the ELC file to test if byte-compiled code runs as expected.

(print (my-counter)) ;; 1
(print (my-counter)) ;; 2

(print (my-another-counter)) ;; 1
(print (my-another-counter)) ;; 2
4

3 に答える 3

1

gnu.emacs.help で回答したように、(defalias 'foo (lambda ...)) を使用してその制限を回避できます。そして、その制限は Emacs の開発コードで取り除かれています。

于 2012-08-22T15:38:57.140 に答える
1

このコードは、少なくとも Emacs 24.1.1 ではうまくバイトコンパイルされません。次のコードをfoo.elファイルに保存しました。これは、ライブラリによる影響を回避するためsetqに代わりに使用します。incfcl

;; -*- lexical-binding: t -*-

(let ((n 0))
  (defun my-counter ()
    (setq n (1+ n))))

これをバイトコンパイルしようとすると ( M-x byte-compile-filefoo.el)、次の警告メッセージが表示されました。

foo.el:3:1:Warning: Function my-counter will ignore its context (n)
foo.el:3:1:Warning: Unused lexical variable `n'
foo.el:5:11:Warning: reference to free variable `n'
foo.el:5:17:Warning: assignment to free variable `n'

すべてのメッセージは、構成体の本体のコードが、マニュアルが主張するようdefunに周囲のレキシカル変数を参照できないことを示しています。n

実際、バイトコンパイルされたコード ( M-x load-filefoo.elc) をロードして(my-counter)フォームを評価すると、次のようなエラーが発生しました。

Debugger entered--Lisp error: (void-variable n)
  ...

残念ながら、ソース コードの形式で評価したときにコードが機能しているように見える理由はわかりません。

于 2012-08-20T06:01:57.110 に答える
0

上記で行っているように、defun 内からレキシカルスコープ (*) 内の変数を参照することはまったく問題ありません。また、そのマニュアルページの「my-ticker」の例も同様です。

マニュアルに次のような行がありません。

defun または defmacro の本体のコードは、周囲のレキシカル変数を参照できません。

次のようなことを言う必要があります。

defun の本体のコードは、同じレキシカル スコープ内で定義されている場合にのみ、レキシカル変数にアクセスできます。

注:この種のコードのバイトコンパイルの問題に関する他の回答にはコメントがあります。これらは最新の emacs で修正する必要があります。v24.2.50.1 で、このバイトが正しくコンパイルおよびロードされることを確認しました。

于 2012-08-20T04:29:57.023 に答える