42

私はいくつかの状態を保存するためにバッファローカル変数を使用するemacsメジャーモードを書いています:

(defun foo-mode ()
  "My nice major mode"
  (interactive)
  (kill-all-local-variables)
  (setq mode-name "foo")
  (setq major-mode 'foo-mode)
  (set (make-local-variable 'foo-state) "bar"))

(defun foo-change-state ()
  (setq foo-state "baz"))

これは非常にうまく機能し、メジャー モードを使用していないバッファでは、foo-state変数がバインドされないという特性があります (これは、シンボル テーブルが乱雑になるのを避けるため、私の意見では良いことです)。

ただし、このようなコードをバイト コンパイルすると、次の警告が生成されます。

Warning: assignment to free variable `foo-state'

を使用defvarすると警告がなくなりますが、副作用があり、foo-stateどこにでもバインドされるようになりました。これは私の意見では望ましくありません。

すべてのバッファでモード固有の変数をバインドせずに警告を取り除く方法はありますか? それとも、これらの変数をグローバルに宣言するべきではないと思うのは間違っていますか?

4

2 に答える 2

44

あなたが望むことを行う公式の方法は(defvar foo-state). 2 番目の引数がないことに注意してください。また、そのような宣言は、それが見つかったファイル (または関数内で使用されている場合は、見つかったスコープ) にのみ適用されることに注意してください。

于 2012-09-15T02:24:14.077 に答える
39

で変数を宣言しますdefvar。警告を削除する方法は他にありません。これは本当に良い習慣と考えられています。

シンボル テーブルを整然とした状態に保つという意図は価値がありますが、実際にはそうしていません。Emacs Lisp の変数束縛のセマンティクスを誤解していると思いfoo-stateますfoo-mode. そうではありません

Emacs Lisp では、名前 (別名シンボル) はglobalです。foo-stateが初めて評価されるとすぐに、ランタイムは新しいシンボル オブジェクトを作成し、foo-stateこれをグローバル シンボル テーブル (別名obarray) に配置します。ローカル シンボル テーブルがないため、 がどこfoo-stateでどのように評価されるかは問題ではありません。 は、任意の場所で同じシンボル オブジェクトをfoo-state参照します(「シンボルの作成」 を参照)。

各シンボル オブジェクトはコンポーネント (別名セル) で構成され、そのうちの 1 つは変数セルです (シンボル コンポーネントを参照)。 setqシステムの現在のバインディングを変更します。レキシカル バインディングのないトップレベルで、シンボル オブジェクトの変数セル、つまり変数のグローバル値を効果的に変更します。setq繰り返しますが、どこで評価されるかは問題ではありません。実際、一部がbar-mode評価された場合(setq foo-state "bar")、もfoo-state"bar" にバインドされfoo-mode、その逆も同様です。

したがって、(defvar)over (setq)it の唯一の効果は、シンボルをグローバル変数として使用する意図を文書化するため、 の動作の操作が意図されていない限り、この変数を変更しないように他の人に伝えますfoo-mode。ドキュメントを変数に添付し、バッファで定義されていることをマークできます (C-h v foo-state定義にジャンプするためのリンクが提供されます)。

Emacs Lisp には名前空間がなく、デフォルトで動的にスコープが設定されているため、モジュール間の競合を避けるためにドキュメントは基本的に重要です。bar-modeusing yourを書いた場合、foo-mode誤って にバインドしfoo-state、呼び出しfoo-change-stateて、変数が意図せずに上書きされたため、モードが正しく動作していないことがわかります。宣言foo-stateしてもこれが不可能になるわけではありませんが、少なくともエラーをキャッチできます。C-h v foo-stateこれは、この変数が別のモードで使用されていることが明らかになるためです。そのモードを本当に操作するつもりがない限り、使用しない方がよいでしょう。

最後に: 前述のすべてのテキストで、「モード」は Emacs Lisp ファイルに置き換えることができます。 modes記号に関しては特別なことではありません。上記のすべては、モードを宣言せず、一連の関数を含むだけの Emacs Lisp にも当てはまります。

于 2012-09-14T21:53:00.417 に答える