defvarの再評価を強制するにはどうすればよいですか?


21

次を含むEmacs lispバッファーがあるとします。

(defvar foo 1)

eval-last-sexpまたはを呼び出すとeval-bufferfoo1にバインドされます。その後、このバッファーを編集して次のようにします。

(defvar foo 2)

eval-last-sexpまたeval-buffer、この行を再実行しないでくださいfoo。まだ1です。

このようなステートメントが複数あり、再評価されていない行を追跡する必要がある場合、これは特に困難です。

Emacsを再起動するだけ(require 'foo)で、それからを見てみましたが、古い.elcファイルをロードしないように注意する必要があります。

現在のファイルで定義されている変数と関数が、新しいEmacsインスタンスにコードを新たにロードするのと同じ状態にあることを、絶対に確実に確認するにはどうすればよいですか?


それをしないと、「Emacsが新しいEmacsインスタンスにコードを新たにロードするのと同じ状態にあることを絶対的に確実に確信する」ことができません。これと他のグローバル変数のみを確認したい場合は、バッファのコードを使用してそれらの値を削除し、コードを再評価できます。makunbound
ドリュー

確かに、(愚かなコード)のような副作用は、(incf emacs-major-version)繰り返し発生することに耐えることができます。たくさんのdefvarフォームを使ってコードをハッキングすることに興味があります。
ウィルフレッドヒューズ14年

回答:


29

他の回答で説明したdefvarように、を使用eval-last-sexpしてフォームを評価してもデフォルト値はリセットされません。

代わりに、eval-defun(デフォルトC-M-xでinにバインドされemacs-lisp-modeた)を使用して、特別な例外として必要な動作を実装できます。

現在のdefunが実際にdefvarorの呼び出しである場合defcustom、この方法で評価すると、変数が既に他の値を持っている場合でも、初期値式を使用して変数がリセットされます。(通常、値が既に存在する場合は変更しないdefvardefcustomください。)


バッファの内容全体を評価する必要がある場合は、トップレベルのフォームを順番に調べeval-defun、それぞれを呼び出す関数を作成できます。このような何かが動作するはずです:

(defun my/eval-buffer ()
  "Execute the current buffer as Lisp code.
Top-level forms are evaluated with `eval-defun' so that `defvar'
and `defcustom' forms reset their default values."
  (interactive)
  (save-excursion
    (goto-char (point-min))
    (while (not (eobp))
      (forward-sexp)
      (eval-defun nil))))

5
これが答えです。偽のdefvarや追加のsetqは必要ありません。のeval-defun代わりに使用しますeval-last-sexp eval-defunバッファ内のすべてのフォームを呼び出す関数を作成し、の代わりに使用することもできeval-bufferます。
マラバルバ14年

1
@Malabarbaこの投稿は、質問に答えるには程遠いです。eval-defun代わりにを使用してください。eval-last-sexpただし、難易度はですeval-buffer
ジル「SO-悪であるのをやめる」14年

@Gillesはい、そうです。eval-defunバッファ内のすべてのトップレベルフォームを呼び出す@Malabaraのアイデアの暫定的な実装を追加しました。
ffevotte 14年

1
defvarが内にない場合、このアプローチは機能しないようですdefun。例:(progn (defvar foo "bar"))
カウ

2
@kaushalmodi引用する後者の例(正規表現を格納する変数)は、候補のように見えますdefconst(常に再評価されます)。最近、このトピックに関する非常に啓発的な投稿が無限の括弧内にあります
-ffevotte

5

他の答えが言うように、これはdefvarが機能する方法にすぎませんが、それを回避することができます、これは結局elispです。

必要に応じてdefvarの動作を一時的に再定義し、その間にリセットしたいパッケージをリロードできます。

ボディの評価中に、defvars値が常に再評価されるマクロを作成しました。

(defmacro my-fake-defvar (name value &rest _)
  "defvar impersonator that forces reeval."
  `(progn (setq ,name ,value)
          ',name))

(defmacro with-forced-defvar-eval (&rest body)
  "While evaluating, any defvars encountered are reevaluated"
  (declare (indent defun))
  (let ((dv-sym (make-symbol "old-defvar")))
    `(let ((,dv-sym (symbol-function 'defvar)))
       (unwind-protect
           (progn
             (fset 'defvar (symbol-function 'my-fake-defvar))
             ,@body)
         (fset 'defvar ,dv-sym)))))

使用例:

file_a.el

(defvar my-var 10)

file_b.el

(with-forced-defvar-eval
  (load-file "file_a.el")
  (assert (= my-var 10))
  (setq my-var 11)
  (assert (= my-var 11)
  (load-file "file_a.el")
  (assert (= my-var 10))

注:これは、defvarを再評価する目的でのみ使用する必要があります。再評価するときにdocstringを無視するだけです。マクロを変更して、docstringを適用する再評価をサポートすることもできますが、それはあなたにお任せします。

あなたの場合、あなたはできる

(with-forced-defvar-eval (require 'some-package))

しかし、elispを書く人がdefvarが指定どおりに動作することを期待していることを知っています.defvarを使用して定義し、init関数でsetqを設定して値を指定することができるため、意図しない変数をnil'ingする可能性がありますが、これはおそらくまれです。

代替実装

これを使用すると、defvarをグローバルに再定義し、新しいdefvar-always-reeval-valuesシンボルの値を変更してシンボルが定義されている場合でも、シンボルの値をINIT-VALUE引数に設定するかどうかを制御できます。

;; save the original defvar definition
(fset 'original-defvar (symbol-function 'defvar))

(defvar defvar-always-reeval-values nil
  "When non-nil, defvar will reevaluate the init-val arg even if the symbol is defined.")

(defmacro my-new-defvar (name &optional init-value docstring)
  "Like defvar, but when `defvar-always-reeval-values' is non-nil, it will set the symbol's value to INIT-VALUE even if the symbol is defined."
  `(progn
     (when defvar-always-reeval-values (makunbound ',name))
     (original-defvar ,name ,init-value ,docstring)))

;; globally redefine defvar to the new form
(fset 'defvar (symbol-function 'my-new-defvar))

1
の振る舞いを再定義するのdefvarが良い考えかどうかはわかりません。には、defvarセマンティクスがわずかに異なるいくつかの異なる使用法があります。たとえば、マクロが考慮していない用途の1つ(defvar SYMBOL)は、値を設定せずに変数の存在をバイトコンパイラに通知するために使用されるフォームです。
ffevotte 14年

絶対にdefvarマクロで再定義する必要がある場合は、おそらく、元のdefvarフォームをmakunboundで置き換えるのではなく、接頭辞としてaを付ける方が良いでしょうsetq
ffevotte 14年

はい、それはひどいアイデアであり、スクラッチバッファでロードされたパッケージのdefvarsを再評価するような場合にのみ使用する必要があります。このようなものを出荷しないでください。
ジョーダンビオンド14年

@Francescoもmakunboundバージョンについては正しいです。私はそれを実装していましたが、アイデアから外れて、代わりにそのコードを答えに入れました。
ジョーダンビオンド14年

3

defvar評価され、あなたが指定した正確に何をしてされています。ただし、defvar初期値のみを設定します。

オプションの引数INITVALUEが評価され、SYMBOLの値がvoidの場合にのみ、SYMBOLの設定に使用されます。

したがって、目的を達成するには、再評価する前に変数のバインドを解除する必要があります。たとえば、

(makunbound 'foo)

またはsetq値を設定するために使用します、例えば

(defvar foo nil "My foo variable.")
(setq foo 1)

ここでdocstringを指定する必要がない場合は、defvar完全にスキップできます。

本当にdefvarこれを使用して自動的にバインドを解除したい場合defvarは、現在のバッファー(またはリージョン、最後のsexpなど)で呼び出しを見つける関数を作成する必要があります。makunboundそれぞれを呼び出します。そして、実際の評価を行います。


eval-buffer最初にすべてのバインドを解除するラッパーで遊んでいましたが、@ Francescoの答えeval-defunはまさにあなたが望むものです。
グルーカス14年

1

次のマクロはeval-defun、特定のバッファの領域を評価する必要がなくなるように、そのサポート関数にトレースして変更することにより作成されました。関連するスレッドで式を文字列に変換するのに助けが必要だったので、@ Tobiasが助けになりました。不完全な関数をマクロに変換する方法を教えてくれました。先にeval-sexp-add-defvars進む必要はないと思いますelisp--eval-defun-1が、誰かがそれが重要だと思ったら、教えてください。

;;; EXAMPLE:
;;;   (defvar-reevaluate
;;;     (defvar undo-auto--this-command-amalgamating "hello-world"
;;;     "My new doc-string."))

(defmacro defvar-reevaluate (input)
"Force reevaluation of defvar."
  (let* ((string (prin1-to-string input))
        (form (read string))
        (form (elisp--eval-defun-1 (macroexpand form))))
    form))

0

問題は、回線が再評価されていないことではありません。問題はdefvar、変数とそのデフォルト値を定義することです。変数が既に存在する場合、デフォルト値を変更しても現在の値は変更されません。残念ながら、setq値を更新する変数ごとにを実行する必要があると思います。

これはやり過ぎかもしれませんがfoo、新しいデフォルト値に簡単に更新できるようにしたい場合は、このようにファイルを更新できます。

(defvar foo 2)
(setq foo 2)

ただし、コードの2つの場所でデフォルト値を維持する必要があります。これを行うこともできます:

(makunbound 'foo)
(defvar foo 2)

しかし、foo他の場所で宣言されている可能性がある場合、対処するいくつかの副作用があるかもしれません。


複雑なモードへの変更をテストしようとすると、それは困難です。コードを変更したくない。特別にeval-defun扱うdefvarので、確かにバッファ全体に似たものがありますか?
ウィルフレッドヒューズ14年

@WilfredHughes「バッファ全体の何か」とはどういう意味かわかりません。makunbound現在のバッファで宣言された変数を再評価する単一の関数が必要ですか?独自に作成することもできますが、このためにすぐに使用できる機能はないと思います。編集:気にしない、あなたが言っていることを理解する。eval-defunバッファ全体で動作します。@JordonBiondoがその解決策を持っているようです。
nispio 14年

いいえ。問題再評価の欠如defvarです。変数がすでに値を持っている場合は何もしません(そのドキュメントによると:)The optional argument INITVALUE is evaluated, and used to set SYMBOL, only if SYMBOL's value is void.。問題は、現在の値ではなくデフォルト値を変更することではありませんdefvar(defvar a 4) (default-value 'a) (setq a 2) (default-value 'a); その後C-x C-edefvarsexpの後; その後(default-value 'a)C-x C-eeval-regionと上のようなdefvarS式ではないではないデフォルト値を変更します。
ドリュー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.