原です。

 >けいじゅ@日本ラショナルソフトウェアです.

なんかお盆明けてみると、ずいぶん情勢が変わっていて、、、

 >int/intですが, やはり結果はRationalにするのがよいと強く思うようになりま
 >した.

うっ。まじっすか。(^_^;

 >CommonLispもそうであったというのに力付けられました(^^;;; それに,
 >Smalltalkもそうだったしたし...

そうなのかあ。CommonLisp と Smalltalk ってがんばってるんだなあ。

 >1. int/int -> rational だとパフォーマンスが悪いという人がいるが, Rubyで
 >   はそういうことを気にするよりは, 分りやすさを気にすべき. パフォーマン
 >   スを気にする人は、明示的に整除するかto_fすればよい.

rational のパフォーマンスで一番気になるのは、演算を行うたびに gcd 
の計算をするところですよね。gcd ってのはかなり複雑な計算なんで、
それが Integer#/ の様な基礎的な位置にある演算子で、特に意識しない
まま始まってしまうってのがやや気持ち悪いわけです。

ちょっと話それるけど、rational.rb では計算するたびに約分してます
よね。もしかすると、ある程度分子と分母が大きくなってからとか、表
示する時に約分する事にするとパフォーマンスが上がるってことありま
せんかね。


 >2. int/int -> floatを主張する人も, rationalならまだ納得するはず. という
 >   か, 文句はあってもあきらめるに違いない.

「int/int -> float」派は、この際論外という事で。:-)

「int/int -> rational」派にも2つあると思うのですけど、一つは 
1 / 1 を rational の 1 とするのと、もう一つは 1 / 1 を int の 1 
とするのと。

流れからすると、石塚さんと正木さんは後者ですね。後者だとすると例
えば 1/3 + 2/3 も int の 1 になるわけですね。それはちょっと気持ち
悪い。

というのは、まず、演算結果のクラスがその結果の様子によってころこ
ろ変わるのが気持ち悪い。クラスをハンドリングするツール系のクラス
で無い限り、メソッドは基本的には一定のクラスを返してほしい。もっ
とも、Ruby にはすでに Bignum と Fixnum と2つの Intger のサブクラ
スがあって、相互変換しているけど、こっちはその変換が頻繁に起こる
わけでもなく、API も完全に一致しているので、話は別であると思う。


また、そのような仕様にすると、各演算では計算の最終段階で結果がど
のクラスに属すべきかチェックを入れなければいけなくなる。これがパ
フォーマンスを落とす。それに例えば、ある数が実は整数であるという
事実が大きな定理だったり、未解決問題だったりするわけです。まあ、
有理数で考える分にはそんなたいそうな事もないのですが、そもそもそ
の様な「判断」を演算がするってのは、やりすぎの気がします。

それから、数学での習慣として、定義域だけではなく、値域も指定して
はじめて写像の定義となるわけで、メソッドを写像になぞらえるなら
(このなぞらえが妥当かどうかという問題はあるが)、結果によってク
ラスが変わるのは、値域がふらふらしているという感覚があって、気持
ちが悪い。

もちろん、可能な値域全てに関して明確な共通仕様があればいいという
考えはあり得るが、それならそれを一つのクラスとして、表現すべきで
は。例えば Integer を無くして、Ratinal のみにすべき、って事になら
ないでしょうか。

「int/int -> rational」派には、「数は一つ」という考えのが根本にあ
ります。これは非常に健康的な考えで、なんとかそれを実現しようとが
んばるのは意義のある事だとは思うのですが、実現にはかなりしんどい。
一方、今の数学的な発想からすると、数は単独であるわけではなく、ま
ず集合があって更に要素間の演算等が定義され、「系」を形作って初め
て意味が出て来る訳です。

ある演算の結果が 1 だったら整数、1/2 だったら、有理数という訳だけ
れど、1/2 だからといって有理数体 Q の元だと決めつけていいのでしょ
うか。整数と有理数の間には Z(p) = {分母が素数pと素な有理数} みた
いのもあります。1/2 は Z(3) とも Z(5) とも思えるわけで、1/2 にとっ
て Rational が特別に選ばれるべきクラスとは言えない。(言えないけ
ど「借りに」言ってしまうことに意味が無いとは言えないけど。)むし
ろ潔く、演算の結果のクラスはあらかじめ固定しておくべきではないで
しょうか。

Z(p) なんてあまり馴染みないですね。例えば(i = Complex(0,1) とし
て) (2 + 2*i) / (1 + i) はどうですかね。「int/int -> rational」
派的には、Fixnum の 2 でしょうけど、(-2 + 2*i) / (1 + i) は 
Complex(0, 2) になる。似たような数の演算をして、片や非常に小さい
クラス Fixnum だが、片や非常に大きいクラス Complex になる。2 と 
Complex(0, 2) をこんなにひどく差別して:-)、Complex(0, 2) と
Complex(0.0, 2.0) をひとし並みに扱って、「自然な」演算と言えるの
でしょうか?自然さを追求するなら ComplexFixnum を作りたくならない
か、、、でもそれをやっていくときりがない。

一方「int/int -> int」派は「数はどの系(クラス)に属するかによっ
て、演算の意味は違う」と、最初からあきらめているわけです。自然で
あることに対してあきらめのいいのが現代的であると考えている。:-)


 >3. matrix.det みたいなものは, 今のメカニズムではどうやっても綺麗に解決しそ
 >   うにない.

これは綺麗に解決しないでいいのではないかなあ。

デフォルトの det は効率が悪くても割り算を利用しない方法を提供して、
さらに要素の状況によっては効率のいい方法をユーザー選べるようにし
たらいいと思います。結局、効率の問題で不細工になるのは、よくある
事でしかたがない。本気で効率を考えるユーザーは、自分でアルゴリズ
ムを選びたいって要求もあるだろうし。


 >4. 今回のcomplex#**なんかも根にはこれがある. 

a ** b の話は / とは大分違いますね。** は T op T -> T に拘る必要
はないですよね。

環の枠組みで決まる場合、つまり b が非負整数、a が 1、a が -1 で b 
が分母が奇数の有理数、、、などの場合は自明な値を返すべきだけど、
それ以外、例えば 2**(-1) の場合は、例外にする(1 / 2 と解釈しない)
かあるいは 0 にする、というのはありだと思います。これを「不自然」
という考え方もありますが、不自然なのは 2**(-1) の「値」ではなく、
整数のカテゴリで 2**(-1) を「させる事」の方なんだ、という考え方を
します。

(負のfloat) ** float は、require "complex" しても NaN のまま、あ
るいは例外でいいと思います。それはやはり「本来は複素数」という考
えをあまりしたくないからです。「本来」って何?と疑問に思います。
Complex(負のfloat) ** float がそれなりの値を返したとしても、それ
は本来の値を返したというより、むしろ Complex の仕様に過ぎないと考え
た方がいいのではないでしょうか。もちろんその仕様はなるべく「自然」
なものを選ぶべきだけど、そもそも「本来」や「自然」の定義が疑わし
いわけだから、そんなに拘ることもないと思う訳です。

Math.sqrt も同様です。今の所、これの戻り値は Float 固定でいいと思
います。だって、C のライブラリに依存しているわけでしょう。もっと
も Float.sqrt などを入れて、Math.sqrt が適宜いい値を返してくれて
も面白いと思いますが。

ところでこれも別の話だけど、平方根を越えない最大の整数を返す、
Integer.sqrt ってのもあると便利かなあ。Math.sqrt(2) を 2 乗したと
き 2 より大きいのか小さいのか、その情報が需要な局面てのもあるから。

いろんな事言ってますが(^^;、結局私の意見としては、「int/int ->
rational でがんばってみるのもいいかもしれないが、int/int -> int 
という現状の仕様のままでも、それはいささかも Ruby の価値を落とし
てはいない(数学的にも)。Rational は早く C で書き直して組み込み
にして頂戴、よろしくお願いします。」というものです。


#どさくさ紛れの要望ですが、Rational(1, 2).inspect は 
#"Rational(1, 2)" じゃなくて "1/2" にしません?