« ^ »

Emacs Lispで関数を一時的に上書きする

所要時間: 約 2分

プログラムを書いていると呼び出す関数の内部で呼び出される関数を一時的に変更したい事がある。変数であれば let 等を使って一時的に上書きする事ができる。しかしEmacs LispはLISP-2なので変数と関数でシンボル表が分かれている。そのため関数を部分的に変更す事は少し事情が異なる。関数のシンボル表を上書きするには fset を使う。ただし、これは let のように一時的に変更する訳ではない。そこで unwind-protect を使い元に戻す処理を書く事で、一時的に関数のシンボル表を変更する。

例えば次の testing-bar 関数と testing-baz 関数を考える。 testing-bar 関数は "bar" という文字列を返す。 testing-baz 関数は "baz" という文字列を返す。

(defun testing-bar ()
  "bar")

(defun testing-baz ()
  "baz")

先程の testing-bar 関数を呼び出す testing-foo 関数を考える。

(defun testing-foo ()
  (testing-bar))

この関数は内部で testing-bar 関数を呼び出しているため、当然 "bar" という文字列を返す。

(testing-foo)
bar

ここで testing-foo 関数のコードを変更する事なく、 testing-bar の代りに testing-baz を呼び出したいとする。ただし、それはずっとではなく今回限りとする。

その場合は一時的に関数のシンボル表を上書きし、処理を実行、その後シンボル表を元に戻す事で実現できる。置き換え対称のシンボルから関数を取得し、元の情報として保存しておく。 fset で置き換えを行った後、目的の処理を呼び出す。そのため次のEmacs Lispは testing-baz が呼び出される事になり "baz" を返す。

(let ((original-fn (symbol-function #'testing-bar)))
  (unwind-protect
      (progn
	(fset #'testing-bar (symbol-function #'testing-baz))
	(testing-foo))
    (fset #'testing-bar original-fn)))
baz

その後 unwind-protect の後処理として置き換えた関数のシンボルを元に戻す。再度 testing-foo を呼び出すと、元に戻ったシンボルから testing-bar 関数が呼び出されるため bar を返す。

(testing-foo)
bar