プログラムを書いていると呼び出す関数の内部で呼び出される関数を一時的に変更したい事がある。変数であれば 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