読者です 読者をやめる 読者になる 読者になる

雨谷の日和

過去11年で1,500を超えるアニメの第1話だけは見続けた僕のお勧めアニメがハズレなはずがない

分数での掃き出し法による行列式計算5

ボヤキ - 技術

さていよいよ四則演算の実装に取り掛かろうと思います。
分数の場合、足し算や引き算よりも掛け算や割り算の方がより基本的な操作なので、まずはそちらから実装してみます。
足し算とかする際、掛け算や割り算を使いますからね……。
さて、一言に掛け算と言っても分数と分数の掛け算と、分数に整数を掛ける掛け算とがあるわけです。分数と分数の掛け算の際には、分数への整数の掛け算を使いますから、ここは最初に整数との掛け算を実装しておくのが良いような気がしますね。
こんな感じかなぁ?
ソースコード

  public Fraction multiply(long dst){
    if(dst == 0)return ZERO;
    if(isZero() || isInfinity() || dst == 1)return this;
    Fraction temp = new Fraction(numerator, denominator);
    if(dst < 0 && temp.numerator < 0){
      dst = Math.abs(dst);
      temp.numerator = Math.abs(temp.numerator);
    }else if(dst < 0 && temp.denominator < 0){
      dst = Math.abs(dst);
      temp.denominator = Math.abs(temp.denominator);
    }
    if(temp.denominator % dst == 0){
      temp.denominator = temp.denominator / dst;
    }else{
      temp.numerator = temp.numerator * dst;
    }
    return temp;
  }

まず、掛ける整数(dst)が0 だったら、問答無用で定数ZERO を返します。
次に、自身がZERO 、INFINITY か、dst が1 だったら掛け算の結果として自分自身を返せばいいでしょうか。
上記のどれにも当てはまらない場合には何かしらの計算が必要になるので、計算結果のためのFraction モジュールのインスタンスをtemp という名前で生成しておきます。
次に、dst が負の数だった場合、分子(numerator)か分母(denominator)が負の数かどうかを調べ、可能なら正の数に直しておきます。うーん。でも、この処理は必要ないかも知れませんねぇ。
最後に実際の計算をします。
分母がdst で割り切れる場合には、割り算の結果を新たな分母とします。
割り切れない場合、分子にdst を掛けたものを新たな分子として採用します。
計算結果をreturn で返せば、掛け算の処理は終了ですね。


この新しく作った掛け算の関数を実際に使ってみましょう。
FractionTest モジュールに、以下のようなコードを追加して試して見ます。
ソースコード

    { //3 を掛けてみる
      Fraction result = fraction.multiply(3);
      print(result);
    }
    { //-2 を掛けてみる
      Fraction result = fraction.multiply(-2);
      print(result);
    }
    { //0 を掛けてみる
      Fraction result = fraction.multiply(0);
      print(result);
    }

実行結果は以下のようになります。

result: 3/2 [false, false]
result: 1/-1 [false, false]
result: 0/1 [true, false]

なんとなく、ちゃんと実装できているような気がしますね。気のせいかも知れませんが。


ところで上記の計算は全てtemp に対して行い、元々のFraction インスタンスの値は編集しません。
以後、処理を実装する際にはすべてそういう方針で行います。一度コンストラクタで設定されたprivate な変数の値は、一切変更しないことで、色々と便利に使える場面が出てくるというのがその理由です。(こういう実装ポリシーを何と呼ぶのかは知りませんが、私はこのアプローチが好きです)
「そんな事言って、さっきの掛け算関数の中でtemp 変数のprivate 変数の値を書き換えているじゃないか」というツッコミもあるかも知れませんが、自身の処理関数の中で生成した自身と同じクラスのprivate 変数は特別扱いということでお願いします。
なお、上記の理由から、私はgetter 関数は作りますがsetter 関数はなるべく作らないようにしていたりします。JavaBeans とかいう実装ポリシーとは全く相容れないポリシーではありますが、私の経験上、JavaBeans のアプローチよりも適用範囲が広いポリシーであると考えています。
まあ、そんな細かなポリシーなどどうでもいいっちゃどうでも良い訳ですが。
次は、整数での割り算を実装することになるかと思います。