module pind.samples.ja.operator_overloading.operator_overloading_solution_1; import std.exception; import std.conv; struct Fraction { long num; // 分子 long den; // 分母 /* 便宜上、コンストラクタは分母にデフォルト値 * 1を使用する。 */ this(long num, long den = 1) { enforce(den != 0, "The denominator cannot be zero"); this.num = num; this.den = den; /* 分母が常に正であることを保証することで * 一部の演算子関数の定義を簡略化できる。 * */ if (this.den < 0) { this.num = -this.num; this.den = -this.den; } } /* 一項 -: この分数の負の値を返す。 */ Fraction opUnary(string op)() const if (op == "-") { /* 単に匿名オブジェクトを構築して返す。 * */ return Fraction(-num, den); } /* ++: 分数の値を1増やす。 */ ref Fraction opUnary(string op)() if (op == "++") { /* ここでは'this += Fraction(1)'を使用することもできた。 */ num += den; return this; } /* --: 分数の値を1減らす。 */ ref Fraction opUnary(string op)() if (op == "--") { /* ここでは'this -= Fraction(1)'を使用することもできた。 */ num -= den; return this; } /* +=: 右側の分数をこの分数に加える。 */ ref Fraction opOpAssign(string op)(Fraction rhs) if (op == "+") { /* 加算式: a/b + c/d = (a*d + c*b)/(b*d) */ num = (num * rhs.den) + (rhs.num * den); den *= rhs.den; return this; } /* -=: 右側の分数をこの分数から引く。 */ ref Fraction opOpAssign(string op)(Fraction rhs) if (op == "-") { /* ここで、既に定義されている演算子+=と * 一項演算子-を使用している。代わりに、減算 * 式を、上記の * +=演算子と同様に明示的に適用することもできる。 * * 減算の公式: a/b - c/d = (a*d - c*b)/(b*d) */ this += -rhs; return this; } /* *=: 分数を右辺で乗算する。 */ ref Fraction opOpAssign(string op)(Fraction rhs) if (op == "*") { /* 乗算式: a/b * c/d = (a*c)/(b*d) */ num *= rhs.num; den *= rhs.den; return this; } /* /=: 分数を右辺で割る。 */ ref Fraction opOpAssign(string op)(Fraction rhs) if (op == "/") { enforce(rhs.num != 0, "Cannot divide by zero"); /* 除算式: (a/b) / (c/d) = (a*d)/(b*c) */ num *= rhs.den; den *= rhs.num; return this; } /* 二項演算子 +: この分数と右辺の分数を足し合わせた結果を。 * 生成する。 */ Fraction opBinary(string op)(Fraction rhs) const if (op == "+") { /* 二項演算子 *: 右辺の分数をコピーし、 * そのコピーに左辺の分数を掛ける。 */ Fraction result = this; result += rhs; return result; } /* 二項演算子 -: 右辺の分数からこの分数を * 引いた結果を返す。 */ Fraction opBinary(string op)(Fraction rhs) const if (op == "-") { /* 既に定義されている-=演算子を使用する。 */ Fraction result = this; result -= rhs; return result; } /* 二項演算子 *: この分数と右辺の分数を乗算した結果を * 返す。 */ Fraction opBinary(string op)(Fraction rhs) const if (op == "*") { /* 既に定義されている *= 演算子を使用する。 */ Fraction result = this; result *= rhs; return result; } /* 二項演算子 /: この分数と右辺の分数を除算した結果を * 返す。 */ Fraction opBinary(string op)(Fraction rhs) const if (op == "/") { /* すでに定義されている/=演算子を使用する。 */ Fraction result = this; result /= rhs; return result; } /* 分数の値をdoubleとして返す。 */ double opCast(T : double)() const { /* 単純な除算。ただし、 * long型の値を除算すると、小数点以下の値が失われるため、 * ここでは'num/den'と書くことはできない。 * */ return to!double(num) / den; } /* 並べ替え順演算子:この分数が * 前にある場合は負の値を、この分数が * 後にある場合は正の値を、両方の分数の * 並べ替え順が同じ場合は0を返す。 */ int opCmp(const Fraction rhs) const { immutable result = this - rhs; /* numはlong型であるため、int型に * 自動的に変換することはできない; 'to'(またはキャスト)によって * 明示的に変換する必要がある。 */ return to!int(result.num); } /* 等価比較: 分数が等しい場合にtrue * を返す。 * * この型では、等価比較を定義する必要があった。 * なぜなら、コンパイラが生成する等価比較は、 * オブジェクトが表す実際の値に関係なく、 * メンバーを1つずつ比較してしまうからだ。 * * たとえば、Fraction(1,2) * とFraction(2,4)の値は0.5だが、コンパイラが生成した * opEqualsは、メンバーの値が異なることを理由に、これらは等しくない * と判断する。 */ bool opEquals(const Fraction rhs) const { /* opCmpの戻り値が0であるかどうかを確認する * だけで十分だ。 */ return opCmp(rhs) == 0; } } unittest { /* 分母が0の場合はスローする必要がある。 */ assertThrown(Fraction(42, 0)); /* 1/3から始めよう。 */ auto a = Fraction(1, 3); /* -1/3 */ assert(-a == Fraction(-1, 3)); /* 1/3 + 1 == 4/3 */ ++a; assert(a == Fraction(4, 3)); /* 4/3 - 1 == 1/3 */ --a; assert(a == Fraction(1, 3)); /* 1/3 + 2/3 == 3/3 */ a += Fraction(2, 3); assert(a == Fraction(1)); /* 3/3 - 2/3 == 1/3 */ a -= Fraction(2, 3); assert(a == Fraction(1, 3)); /* 1/3 * 8 == 8/3 */ a *= Fraction(8); assert(a == Fraction(8, 3)); /* 8/3 / 16/9 == 3/2 */ a /= Fraction(16, 9); assert(a == Fraction(3, 2)); /* 型'double'で同等の値を生成しなければならない。 * * doubleはすべての値を正確に表現することはできないが、 * 1.5は例外であることに注意。そのため、このテストが * この時点で適用されている。 */ assert(to!double(a) == 1.5); /* 1.5 + 2.5 == 4 */ assert(a + Fraction(5, 2) == Fraction(4, 1)); /* 1.5 - 0.75 == 0.75 */ assert(a - Fraction(3, 4) == Fraction(3, 4)); /* 1.5 * 10 == 15 */ assert(a * Fraction(10) == Fraction(15, 1)); /* 1.5 / 4 == 3/8 */ assert(a / Fraction(4) == Fraction(3, 8)); /* ゼロで除算した場合はスローしなければならない。 */ assertThrown(Fraction(42, 1) / Fraction(0)); /* 分子の小さい方が先。 */ assert(Fraction(3, 5) < Fraction(4, 5)); /* 分母の大きい方が先。 */ assert(Fraction(3, 9) < Fraction(3, 8)); assert(Fraction(1, 1_000) > Fraction(1, 10_000)); /* 値の小さい方が先。 */ assert(Fraction(10, 100) < Fraction(1, 2)); /* 負の値の方が先。 */ assert(Fraction(-1, 2) < Fraction(0)); assert(Fraction(1, -2) < Fraction(0)); /* 値が等しいものは、両方とも<= and >=でなければならない。 */ assert(Fraction(-1, -2) <= Fraction(1, 2)); assert(Fraction(1, 2) <= Fraction(-1, -2)); assert(Fraction(3, 7) <= Fraction(9, 21)); assert(Fraction(3, 7) >= Fraction(9, 21)); /* 値が等しいものは、等しくなければならない。 */ assert(Fraction(1, 3) == Fraction(20, 60)); /* 符号も値も等しいものは、等しくなければならない。 */ assert(Fraction(-1, 2) == Fraction(1, -2)); assert(Fraction(1, 2) == Fraction(-1, -2)); } void main() { }