C#でも分数class
https://qiita.com/HitsujiRere/items/e49275a42ddda92b510c
こんな記事を見たのでjavascriptで書いてみた。
が、javascriptの場合は数値が整数とは限らないので、小数を渡されることも考慮する必要がある。
そこでこんな感じになった。
const fraction = class {
constructor(numerator=0, denomitor=1){
this.n = numerator;
this.d = denomitor;
fraction._reduce(this);
}
/** 約分 */
static _reduce(o) {
const m = fraction._integerizable(o.n, o.d);
o.n *= m, o.d *= m; // 小数が渡されたら分子分母を整数化
const g = fraction._gcm(o.n, o.d);
o.n /=g, o.d /=g; // 最大公約数で除算
}
static _gcm(n, d) {
for(let i = Math.min(n,d); i > 1; i--) if(n%i==0 && d%i==0) return i;
return 1;
}
static _integerizable(n, d) {
for(let m = 1; m < Number.MAX_SAFE_INTEGER; m++)
if (m*n%1==0 && m*d%1==0) return m;
throw new Error("overflow while reducing ("+n+","+d+")");
}
toString() { return this.n + (this.d>1?("/" + this.d):""); }
numerize() { return this.n / this.d; }
valueOf() { return this.numerize(); }
add (other){
return new fraction(
this.n * other.d + other.n * this.d,
other.d * this.d
);
}
subtract(other){ return this.add(new fraction(other.n * -1, other.d)); }
multiply(other){ return new fraction(this.n * other.n, this.d * other.d); }
divide (other){ return this.multiply(other.reciprocal()); }
reciprocal(){ return new fraction(this.d, this.n); }
出来上がったらとりあえずテスト。
const test = (arr)=> arr.forEach(f=>{
const t=(a,b)=>{
const stra = a.toString();
const strb = b.toString();
console.log(stra + " * " + strb + " = " + a.multiply(b));
console.log(stra + " / " + strb + " = " + a.divide(b));
console.log(stra + " + " + strb + " = " + a.add(b));
console.log(stra + " - " + strb + " = " + a.subtract(b));
};
t(f.a, f.b); t(f.b,f.a);
});
test([
{ a:new fraction(5), b:new fraction(1,2) },
{ a:new fraction(2,3), b:new fraction(1,6) },
{ a:new fraction(1,2), b:new fraction(1,2) },
{ a:new fraction(1/3), b:new fraction(3) }
]);
今回の気づきとしては「javascriptでは無理数が近似値に有理化されてしまっている」ということ。
[ Math.PI, Math.sqrt(2) ].forEach(e=>{
console.log(e);
const o = new fraction(e);
console.log(o);
console.log(o.numerize());
});
// Math.PI: fraction {n: 245850922, d: 78256779}
// Math.sqrt(2); fraction {n: 131836323, d: 93222358}
ついでに小数の文字列表現からもインスタンス化できるようにしてみたが、
fraction.fromString(exp) {
const arr = exp.split(/[ \/]+/).filter(e=>e.length).map(e=>Number(e));
if(arr.length < 2) arr.push(1);
return new fraction(arr[0], arr[1]);
}
こっちの方が簡単だわな。
fraction.fromString(exp) {
return new fraction(Number(eval(exp)));
}