2014-04-14-01-演算子オーバーロードについても考える - project-enigma

2014-04-14-01-演算子オーバーロードについても考える

>> Site top >> weblog >> 月別アーカイブ >> 2014年04月のlog >> 2014-04-14-01-演算子オーバーロードについても考える

最終更新日付:2014/04/14 23:50:00


演算子オーバーロードについても考える

2014 年 04 月 14 日

数日前の件については、結局メソッドを増やす方法で対処した。これで list-sort とかいった無様なメソッド名がかなり減ったが、まだある。それは C++ で言うところの演算子オーバーロードだ。これも同じ方法で対処しようと考えたものの、ちと厄介な方向に行きつつある。今回は、それについて。

 

まずは現状から。たとえば、コンテナクラスの代入演算子(にあたるメソッド)は現在 container-assign という名前になっている。そして反復子の代入は iter-assign だ。うん、無様だね。これを以前と同じ方法で解決したい、そう思ったわけだ。で、最初に出てくる「どうしよう」は、名前だったりする。

C++ では、演算子のオーバーロードは概ね operator==() みたいな名前で定義する。たとえばクラス Foo の比較演算子であれば、以下のようになる。

bool operator==( const Foo& lhs, const Foo& rhs ) {
    bool ret = false;
    // compare lhs & rhs...
    return ret;
}

 

考えてみれば operator== などというのは長ったらしい名前だが、これは気にするには及ばない。なぜなら、使う場合には f1 == f2 としか書かないからだ。しかし、Common Lisp でこれを真似しようと思えば、メソッド名をそのまま書く必要がある。だからできれば operatorXXX というのは避けたいのだ。

そういうわけなので、とりあえず opr== みたいな名前を導入することにした。基本的には演算子オーバーロードの operator という部分を opr に変更しただけだが、operator!= は opr/= になる。あと、インクリメント/デクリメントについては前置形式が ++opr / --opr で後置形式が opr++ / opr-- になる。やっぱり無様ではあるが、これより良いものを思いつかないのだ。

ひとまずのところ、この方法で作成した総称関数に統合しようと考えているのは以下のものだ。

だがしかし。ここで気付いて当然のことに気付いて嫌な気持ちになる。opr== や opr< といった「標準的な」比較方法ができると、あれに手をつけなければならなくなる。そう、アルゴリズムが要素を比較する時のアレだ。自分はかなり初期の段階で、姑息な逃げの手を打っていた。これに向き合うためにも、以下にまとめよう。

たとえば、アルゴリズム replace は STL では以下のようになっている。

template<class ForwardIterator, class T>
void replace( ForwardIterator first, ForwardIterator last,
              const T& old_value, const T& new_value);

 

これは、範囲 [first, last) 内の要素について、ぞれが old_value に等しければそれを new_value に置き換える。問題は、「等しければ」という言葉の意味だ。STL では operator==() が使われる。では、CL-STL の現状はどうなっているだろうか。以下のように、&optional なパラメータ eql-bf が追加されている。

(defmethod replace ((first forward-iterator)
                    (last  forward-iterator)
                     old-val new-val &optional (eql-bf #'eql))
  (...))

 

この可哀相な eql-bf は、当時「演算子オーバーロードにあたるもの」を実現するという考えがなかったため、等値比較にはなんらかの比較関数を与えさせざるをえない、と結論付けたことによるものだ。しかし、opr== のような名前による比較を一般的な枠組みとして扱うことにすれば、このようなやり方で逃げを打つ必要はなくなるだろう。

ここまでを簡単にまとめよう。まず、opr== などの総称関数を作成し、デフォルトの比較方法として妥当な(、と思われる)実装をいくつか提供しておく。

(defgeneric opr== (a b))

(defmethod opr== (a b)
  (eql a b))

(defmethod opr== ((a fixnum) (b fixnum))
  (= a b))

(defmethod opr== ((a string) (b string))
  (string= a b))

そして、CL-STL のコンテナや反復子などは、個別に defmethod しておく、と。また、CL-STL が関知しないユーザ定義タイプでも CL-STL:opr== のメソッドで特定化することは可能だから、コンテナの要素となるようなものについて定義しておき、アルゴリズムに渡す、ということが可能になる。うん、ここまではいい。

しかし、さらに考えていくと気が重くなっていく。CL-STL では、とりあえずコンテナに入れる要素は全て、C++ でいうところのポインタのイメージでいた。しかし、代入に使用する演算子を標準化できるとなると、そうも言っていられなくなるのだ。だがこれについては長くなりそうだ。日を改めて書くことにしよう。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : Common Lisp

Page tag : STLとその移植

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.