@kyanny's blog

My life. Opinions are my own.

add-to-list を読んでみたがわからなかった => 読んでみて少しわかった

subr.el という Emacs に標準でついてくるものの中にある add-to-list という関数を使うのが load-path への追加のときにベターな選択肢のようなので、どんなことがかいてあるのかみてみましたが、見慣れない単語(シンボル?)がたくさんでてきてよくわかりませんでした。 null というのは nil と同じかなと予想できて、 member というのもリストに要素が含まれているかの真偽値を返すのだろうと思いましたが、 memq とか memql というのはなんなんだろう。とりあえず一つずつ C-h f とかで引いていくことにします。

ヘルプを引きつつ読んでみたら、だいたいわかりました。

(add-to-list load-path "~/.lisp" t 'eq)

のように呼ぶ(呼べる)もので、三つめと四つめの引数は省略できる。 compare-fn というのは、 element が list-var に含まれているかどうかをチェックする比較関数を選んだり、自分で渡すことができる。省略すると(デフォルトでは) (member) が (equal) で比較していて equal は文字列として中身が等しければ non-nil を返す。 'eq や 'eql は同じ lisp オブジェクトかどうかまでチェックし、 浮動小数点の比較をするときに eq ではなく eql を使うと良いらしい。第三引数が non-nil ならばリストへの追加に append を使い、そうでなければ cons を使う。結果として、デフォルトでは同じ要素がなければ cons で追加する、という働きをする。

一つわからないのが let のところの while などと、そのあとに (symbol-value list-var) を一回呼んでいるあたり。funcall というのは無名関数を実行するもの、なんだろうか。 eval みたいなものだろうか。いまこれ以上深入りすると煙が出てきそうなのでここまでにしておく。

というわけで、読んでみたおかげでほんのすこしだけ Emacs Lisp の理解が深まりました。

(defun add-to-list (list-var element &optional append compare-fn)
  "Add ELEMENT to the value of LIST-VAR if it isn't there yet.
The test for presence of ELEMENT is done with `equal',
or with COMPARE-FN if that's non-nil.
If ELEMENT is added, it is added at the beginning of the list,
unless the optional argument APPEND is non-nil, in which case
ELEMENT is added at the end.

The return value is the new value of LIST-VAR.

If you want to use `add-to-list' on a variable that is not defined
until a certain package is loaded, you should put the call to `add-to-list'
into a hook function that will be run only after loading the package.
`eval-after-load' provides one way to do this.  In some cases
other hooks, such as major mode hooks, can do the job."
  (if (cond
       ((null compare-fn)
        (member element (symbol-value list-var)))
       ((eq compare-fn 'eq)
        (memq element (symbol-value list-var)))
       ((eq compare-fn 'eql)
        (memql element (symbol-value list-var)))
       (t
        (let ((lst (symbol-value list-var)))
          (while (and lst
                      (not (funcall compare-fn element (car lst))))
            (setq lst (cdr lst)))
          lst)))
      (symbol-value list-var)
    (set list-var
         (if append
             (append (symbol-value list-var) (list element))
           (cons element (symbol-value list-var))))))