作者: 機械伯爵
日時: 2002/3/22(11:38)
P-4 オペレーターは過負荷?
   〜演算子のオーバーロード是非論〜

 Pythonと出会う前、私が主に使っていた(遊んでいた?)言語はJava
でした。

 BASICはそこそこ、アセンブル言語とCはなんとか使える程度だっ
た私にとって、オブジェクト指向言語とはJavaのことでした。

 名前は知っていましたが、Smalltalkに本当に出会うのは、ずっと
先のことになります*1

 で、そのJavaの解説書が、ことあるごとに目の敵にしていたのが、
この演算子のオーバーロードと多重継承でした*2

 まぁ、多重継承論争はかなり事情が複雑になるので、今回は軽め
に「演算子のオーバーロード」の是非を、少し考えてみたいと思い
ます。

 演算子のオーバーロードができる言語といえば、すぐに思いつく
のがC++ですね(Smalltalkも確か二項メソッドのオーバーロードが
できると思ったのだけど・・・手持ちの本は初心者用なのでちと確
認できてません)。

 とあるJavaの解説書(確かJavaの開発チームのメンバーが書いた
本だと思ったけど)で演算子のオーバーロードについて曰く「私は
そんな機能は使ったことがない」だそうです。

 これは主に、演算子のオーバーロードによる弊害云々という話で
はなく、単にその実装方法がやたら面倒くさいからではないか、と
思います。

 確かに、C++の演算子のオーバーロードは面倒くさいモノです。

 もともと、クラスインスタンスの中のプライベートフィールドに
アクセスすることが多いため、フレンド関数を用いて定義するのが
定石ですが、ここらへんの記述を始めたら、例え後々簡単に記述で
きることがわかっていたとしても「なんでこんな苦労を・・・」と
考えてしまって中止するのが普通でしょう*3

 その上、オブジェクトに対するイメージの捉え方によっては、簡
単どころかかえって混乱を招く可能性もあります。

 これは、プログラミング言語自体が必要以上に多数存在すること
からもわかると思いますが、イメージの捉え方というのは千差万別
であり、また、自分のセンスに合わない方法を押し付けられるのは、
苦痛であり、さらには誤解のもととなるからです*4

 翻って「Pythonに於ける演算子のオーバーロード機能」ですが、
とりあえず「面倒くささ」だけは皆無です。

 もともと型指定の無いスクリプト言語ですから、相手のオブジェ
クトの型をあらかじめ指定する必要はもちろんありません。

 実際の方法としては、演算子のフックになっている組み込みメソ
ッドを設定するだけです。

 ただし、それによってオブジェクトが使いやすくなるか使いにく
くなるかについては、やはりコーディングするプログラマのセンス
にかかっています。

 実際の話、確実性を期するなら、演算子のオーバーロードは、
Pythonでもやはりあまり使わないほうが良いのかもしれません。

 しかし、私は敢えて「やってみたら?」と言いたいと思います。

 例えば私は、以前実験的に「分数(Fraction)」クラスを作って
みました(SmalltalkのFractionクラスがうらやましかったので)

 分数も数値の端くれですから、やはり演算子を使って計算するほ
うがすっきりして見えるでしょう。

 また、数値の一種なので、演算子の使い方については異論の余地
も無いでしょう。

 このように、「数値的」に扱いたいクラスオブジェクトについて
は、演算子のオーバーロードを用いて数値のように扱えるようにす
ることが効果的だと思われます。

 また、コンテナに機能を付加したラッパークラスなども、もとと
なるコンテナで使える演算子はオーバーロードしておくと、本格的
なコンテナとして無理なく使えるでしょう(キーによるアイテム呼
び出しのフックメソッドも勿論あります)。

 Pythonをプロトタイピング用ツールとして使うなら、例えばC++
で後々ライブラリを書くとき、どんな使い具合かを試すにはもって
こいでしょう。

 インデントブロックの時とは逆に、演算子のオーバーロードは多
少なりとも手間が増えますし、基本的には「使わなくても済む」機
能ですので、怠け心では利用できません。

 そのように決意の固い人に対する可能性は、私は残しておいても
いいのでは、と思います。

 また、演算子のオーバーロードを行うことは、言語を拡張する、
すなわち言語の設計を行うことです*5

 本格的に1から設計するのは大作業ですが、今ある言語を拡張す
るのは断然気楽ですし、手間もかかりません。

 また拡張作業は、何も人に使ってもらうためではなく、自分が作
業をやりやすくするためだけに行っても十分価値があります。

 C++だと、労力を考えた場合、よほど大仕事でないとつりあいま
せんが、Pythonなら手間が少ないので、ちょっとした作業の効率化
として行っても、労力の投資に対して十分おつりがでます*6

 余談ですが、Pythonの標準パッケージでは、演算子のオーバーロー
ドが使われているのを見たことがありませんが、これは、標準パッ
ケージのドキュメントを無用に増やさないためだと思われます。

 必要なら、ラッパークラスは簡単につくれますから、そこで装備
すればよいわけです。

 Pythonは、標準的な機能は関数で提供されていますので「もうち
ょっとオブジェクトっぽいのがいい」という方は、自分でクラスを
作って組み込み関数をそのまま自作のクラスに組み込こんでみるの
も面白いですよ。

 そのときには、ついでですから、さまざまな演算子を組み込んで
「それっぽく」すると楽しいかもしれません。

 どうですか? あんまり肩肘はらずに「自分の道具(言語)のデ
コレーション」くらいのつもりで、演算子のオーバーロードをそえ
てみては?



*1 Smalltalkに本当に出会うのは、ずっと先

 当時はオブジェクト指向という言葉が、日本ではあまり理解され
ないまま一段落して、Smalltalkの書籍類が一時消えていました。
 Javaには賛否両論ありますが、C++が使われていながら忘れ去られ
かけていたオブジェクト指向という言葉を、(日本で)もう一度復興
させたという意義があると、私は思っています。


*2 演算子のオーバーロードと多重継承

 もしかして、この2つを知らない方もいるかもしれない(Javaで
は完全に黙殺されてますし)ので、蛇足とは思いますが説明してお
きます。
 多重継承とは、2つ以上のスーパークラスから継承したサブクラ
スをつくることです。
 演算子のオーバーロードとは、要するに"+,-,*,/"といった諸々の
演算子を、新しく作ったクラス(型)から作ったインスタンス(オ
ブジェクト)でも使えるように定義することを意味します。


*3 フレンド関数を用いて定義する

 私自身はC++は嫌いではないのですが、アクセスレベルの段階の多
さの必要性については、未だによく理解できないところがあります。
 特にフレンド関数の定義は、クラス設計時からフレンド関数を組
み込んでおかなければならないことを考えると、非常にプロメテウ
ス的(前話参照)な能力を要求すると思うわけなのですが・・・


*4 自分のセンスに合わない方法を押し付けられるのは、苦痛

 個人的に、C++のiostreamは便利だと思いますが、<<を挿入の演算
子として使用するのはあまり好きではありません・・・といいなが
ら、Pythonの新しい記法ではコレに近いことやってるんだよな
ぁ・・・


*5 言語の設計を行う

 同様に、言語設計(拡張)を手軽に行える言語として、tclやForth
があります。
 特にForthは「言語設計をやるなら、かならずForthを使ってみる
こと」と一部で言われている(らしい)くらい、言語拡張と密接に
関わりのある(というか、言語拡張しないと、あまり便利に使えな
い)言語です。
 tclは、shellライクの独特な記述方式にちょっとクセがあります
が、コマンドを拡張できるという点では、非常に面白い言語です
(・・・と、tclについては、別のブースがあるので、そちらへどう
ぞ。なんか相方のtkばっかり有名ですが、tclも面白いんですよ)


*6 労力の投資に対して十分おつりが

 ただし、このテの作業は「はまり込み」やすいので、一旦はまる
と、凝りすぎて時間を忘れる、という副作用はあります(まぁ、プロ
グラミング全般に言えることですが・・・面白すぎるんですよね)