プログラミング原人の進化ログ

プログラミング原人の進化論

オレ プログラミング ベンキョウ スル。マナンダ コト カク。

【Common Lisp】defmethodコマンドの使い方

defmethodを使って関数を定義する方法です。

目標

defmethodコマンドを使ってジェネリック関数を定義する。

defmethodの利点

データ型によらないジェネリックな関数を定義する際に便利なのが defmethod です。

例えば、引数を2つとる関数を考え、整数が渡された場合は引数同士を足し、リストが渡された場合は両者を繋げる操作を行いたいとします。 このくらいの複雑さの関数であれば型述語を使って場合分けをしても良いですが、対応すべきデータ型が増えるに従って場合分けが多くなりますし、Lispコンパイラでは性能が落ちるそうです。

ここで出番となるのがdefmethodで、同じ名前で型ごとに関数を定義します。この場合、コンパイラが引数の型を調べ、複数の関数の中から適切なものを選んで実行することになるのですが、これを型によるディスパッチ(type dispatching)と言います。 この方式では、新たな型に対応しなければならない時、既存のコードに手を加えずにその型のための関数を定義できるという利点もあります。

上の例を実際に書いてみます。

CL-USER> (defmethod add ((a number) (b number))
           (+ a b))
#<STANDARD-METHOD (#<BUILT-IN-CLASS NUMBER> #<BUILT-IN-CLASS NUMBER>)>
CL-USER> (defmethod add ((a list) (b list))
           (append a b))
#<STANDARD-METHOD (#<BUILT-IN-CLASS LIST> #<BUILT-IN-CLASS LIST>)>
CL-USER> (add 1 2)
3
CL-USER> (add '(a b) '(c d e))
(A B C D E)

例えば、string型の引数をとりたい、となれば新しく関数を独立に定義することができます。上ではnumberlistを型として与えていますが、組み込み型だけでなく、自分で定義した構造体を指定することもできます。

参考

コンラッドバルスキ(2013)「Land of Lisp」 川合史郎訳 オライリー・ジャパン