けいじゅ@日本ラショナルソフトウェアです. 話しがruby-listの内容じゃなくなっているので, ruby-devに引っ越しません か? 豊福@パパイヤさんは入っていましたっけ? In [ruby-list :06975 ] the message: "[ruby-list:6975] Re: coerce ", on Mar/06 04:43(JST) toyofuku / juice.or.jp writes: > 豊福@パパイヤです。 >メールの津波に押し流されてます。 私もそうです(^^;;; といいつつ, このメイルの話題もほとんどの人にとっては興味が湧かない話題 だと思います. 豊福@パパイヤが ruby-dev に参加されているならそちらに移 動した方が良いと思いますがどうでしょう? >> (Fixnum->Bignum->Float) -> (Rational <-> Complex) -> (Matrix <-> Vector) >> となっています. > > これはしょうがないのですかね。すべてのクラスに >平等にするには足し算のクラスでも作って > >class PlusOp > def PlusOp.plus(x, y) > x と y のタイプのすべての組み合せで場合分け > してそれぞれの足し算を実行 > > # 新しいクラスを作ったらそれに関する足し算を > # このメソッドに追加する > end >end > >の PlusOp.plus を呼ぶようにするくらいしか >思いつきません。 確かにそういう実装もありますね. でも, この様な凝った方針は数式処理シス テムのようなそれが中心の場合だけ採用されることになると思います. >>>Smalltalk ではここらへんどういう態度をとっているの >>>でしょうか。 >> >> もっと単純です. > 簡単なケースしか扱っていないのですね。 単純というか... 通常の数システムのことを良く分析した結果だとは思うんで す. でも, それにより広く考えると収まるとは限らないということですかね. >> ちなみにこの後, 他の技法である. >> * ダブルディスパッチ >> * マルチメソッド(CLOSとか) >> の方法も簡単に説明してあります. > > 聞いてばかりですみませんが、もし簡単なこと >でしたら教えて下さい。 ダブルディスパッチは, オブジェクト指向プログラミングにおける一般的な技 法です. [ruby-list: ??]にちょっとその話題が出ていました. 今回の場合だけに限定して話しますと, class A def r_op(other) ... if もしotherとの演算が分からない場合 other.l_op(self) end end end の用にレシーバと引数を入れ換えてメソッドを呼びだす方法です. これですと, 自分の知らない他者との(左)演算はそちらに回してそちらで(右)演算してもら うことができるようになります. もう1つのマルチメソッドは言語の機能ですので, 今回はそれほど重要ではあ りませんので説明はしませんが, 興味があればCLOSの文献でもあったって下さ い. ただし, それに近いのことはC++でも実現できまして: operator +(integer, integer) operator +(integer, double) operator +(double, integer) operator +(double, double) の様に各々の引数の型に応じて演算を定義することができます. ただ, 大量に メソッド定義をしないとならないのですが... どんな場合でも対応できること は確かです. > C の case と混同していました。break がないから >else の方に流れていくのでは?と。 ですね(@@;;; >> まだ, 今なら本当に必要であるならば変更(のリクエスト)は可能であると思い >> ますので検討の価値はあると思います. > Scalarクラスを使う方法で気になるのは >(1) coerce には演算の区別がないので一つの演算が > Scalarクラスを使うことになると他のすべての > 演算も Scalarクラスを使うことになる。もし別の > 演算がさらに別のクラスを使いたくなると・・・ > ==> coerce の引数にメソッドを追加してはどうか 今回の場合の, Numeric op Matrixの場合は, Scalar op Matrix と一応意味のあるクラスを導入していますが, 一般的にはどのようなクラスで も良いんです. Foo op Bar => WrapClass<Foo> op Bar とするダミーのクラスを用意してそこで各オペレータに応じた適当な演算を提 起すれば良いからです. その時, 演算しに応じて第1引数も第2引数も適当に型 変換できますし, さらに演算子も他のものに交換可能です: class WrapClass<Foo> def op(other) case other when Bar 全然違うクラスへの変換(self) op'(全然違うクラスへの変換(other)) ... end end end ということで, (1)の問題はScalarのような間接クラスを導入する手間はあり ますが, それ以上の問題はないと思います. というわけで(2)に話は進むのですが... >(2) 本当は Numericクラスあたりでやらしたいことを > わざわざ Scalarクラスのオブジェクトを作って > それにやらしている。 > > ==> 適用すべきメソッドを coerce の返値で教えて > やってはどうか > この方法で Matrix 関連を書くと次のようになります。 > 書き方はいい加減ですがやりたいことはわかって >もらえると思います。 (中略) 言いたいことは非常に分かるんですが... 気になることを幾つか述べます. 1. まず, この様な演算子に固有なことをcoerceにやらせるべきかというのが 気になります. coerceにここまでやらせるのではなく, 演算子のことは演算子側にやって もらった方が良ういような気がします. 2. Numericで行列に関する演算が定義されていて, クラスを静的に考え過ぎ ているというのが気になります. というのも rubyは動的な言語なので後か らクラスが追加されていくわけですが, そのたびにNumericにメソッドを定 義するのは気が引けるということです. 今の, Fraction/Complex/Matrixでも既存クラスへの再定義は行なっていま すが, 基本的には追加だけです(mathn.rbは省く). これは, できるだけ既 存クラスの再定義は避けるべきだと私は考えているからです(他の方法があ ればですが). # ついでに言えば, Numeric::* の様な指定はできません. ただし, class Matrix def coerce(other, meth) case meth when :+ case other when Numeric エラー ... end when :* case other when Numeric return self, other, :r_mul ... end ... end end end の様にself, otherを入れ換えて左演算から右演算へ変換してあげればよ いですが. # ダブルディスパッチと同じ方法ですね. この方法の利点は, この機能を導入するとScalarのような間接的なクラスの導入を避けることがで きるのじゃないか? ということですね. (後で話しが出てきますが) num / M の例を考えます. num * M.inverse を行ないたいのですが, class Matrix def coerce(other, op) case op when :/ return other, self.inverse, :* end end で, できますね. これなんかは演算子を変えてかつそれ用の型変換を行なっ ていると考えることもできます. また, num ** M では, Matrixに登録されているメソッドを使って M.powerd_by(num) を行ないたいのですが, これも, class Matrix def coerce(other, op) case op when :** return self, other, :powerd_by end end でできますね. こうやって考えると, この方法は, 簡単な割に結構強力でかつ, 過去との互換 性も大体あるといえます(引数が1つ増えるだけ). というわけで, coerceの self.coerce(other, op) -> (other', self', op') への仕様変更を私も推したいと思います(^^;;; 私はなっとくしたのですが, 松本氏が納得しないことには意味がなくて... い かがでしょう? >> matz -- > ついでに matrix.rb での質問です。 > Scalar::/ (中略) >の _M って何ですか。 うーん. 完全バグですね(^^;;; otherの間違いですね. > Scalar::** (中略) > other.powered_by(self) > >の powered_by って何ですか。 > ここはエラーにしそうなところですが。 >まさか exp(M) を計算して返そうと・・・ 多分その通りです. jordan標準形が求まればexp(M)は求まりますし... ただ, その前提になる固有値を求める部分で挫折してしまってあきらめてしま いました(^^;;; まあ, それはともかく, n ** M を求められるとしてもそれは Numeric(n)で演 算すべきではなく行列側で求めるべきだと思い M.powerd_by(n)を呼び出して いるのでした(まさにダブルディスパッチですね). # 話しは違いますが, complex ** complex, sin(complex)とかは頑張って(適 # 当に)定義しているんですよねえ... ただ, 正しいかどうか分からんので誰 # か検算(?) して欲しいんですが... __ ................................石塚 圭樹@日本ラショナルソフトェア... ----------------------------------->> e-mail: keiju / rational.com <<---