PowerShellで分数を扱う
PowerShellで分数が使いたくなった。
なので、C#で書いてみた。
演算子オーバーロードが意外に書くのが面倒。
ジェネリクスでなんとかできないか?、、、。
スクリプト、、、が長いので先に解説・使い方
June.Fractionクラスにコンパイルします。
基本的に扱うのはintですが、キャストだけはdecimalに対応しています。
.ToString()すると、約分して"n/m"表記にします。
四則演算・比較が使えます。
PS > $a = New-Object June.Fraction 1,2 PS > $a.ToString() 1/2 PS > $b = [June.Fraction]1/3 PS > $b.ToString() 1/3 PS > ([June.Fraction]0.25).ToString() 1/4 PS > ($a + $b).ToString() 5/6 PS > ($a - $b).ToString() 1/6 PS > ($a * $b).ToString() 1/6 PS > ($a / $b).ToString() 3/2 PS > $a -lt $b False PS > $a -gt $b True
なお、PowerShellの比較演算子はIComparable依存の模様。
演算子オーバーロードだけでは怒られた。
どうでも良いけど「[June.Fraction]1/3」の意味は、「intの1をFractionにキャストして3で割る」であって「1/3をFractionにキャストする」ではないけど、後者に見えるのがお気に入り。
スクリプト
if(!('June.Fraction' -as [type])) { Add-Type -TypeDefinition @" using System; namespace June { public struct Fraction : IComparable { #region property public int Numerator { get; private set; } public int Denominator { get; private set; } public int DisplayMultiplier { get; set; } #endregion #region public static method public static int GetGreatestCommonDivisor(int a, int b) { // Euclidean Algorithm int t = a; if (Math.Abs(b) > Math.Abs(a)) { a = b; b = t; } while (b != 0) { t = a % b; a = b; b = t; } if (a == 0) a = 1; return a; } public static bool TryParse(string a, out Fraction b) { bool ret = false; decimal d; string[] c = a.Split('/'); int nu, de; if (decimal.TryParse(a, out d)) { b = new Fraction(d, 10); ret = true; } else if (c.Length == 2 && int.TryParse(c[0], out nu) && int.TryParse(c[1], out de)) { b = new Fraction(nu, de); ret = true; } else { b = new Fraction(); } return ret; } public static Fraction Parse(string a) { Fraction ret; if (!Fraction.TryParse(a, out ret)) { throw new FormatException(); } return ret; } #region operator + public static Fraction operator +(Fraction a, Fraction b) { int gcd = GetGreatestCommonDivisor(a.Denominator, b.Denominator); return new Fraction(a.Numerator * (b.Denominator / gcd) + b.Numerator * (a.Denominator / gcd), a.Denominator * (b.Denominator / gcd)); } public static Fraction operator +(Fraction a, int b) { return new Fraction(a.Numerator + b * a.Denominator, a.Denominator); } public static Fraction operator +(int a, Fraction b) { return (b + a); } public static Fraction operator +(Fraction a) { return a; } #endregion #region operator - public static Fraction operator -(Fraction a, Fraction b) { int gcd = GetGreatestCommonDivisor(a.Denominator, b.Denominator); return new Fraction(a.Numerator * (b.Denominator / gcd) - b.Numerator * (a.Denominator / gcd), a.Denominator * (b.Denominator / gcd)); } public static Fraction operator -(Fraction a, int b) { return new Fraction(a.Numerator - b * a.Denominator, a.Denominator); } public static Fraction operator -(int b, Fraction a) { return new Fraction(b * a.Denominator - a.Numerator, a.Denominator); } public static Fraction operator -(Fraction a) { return new Fraction(-a.Numerator, a.Denominator); } #endregion #region operator * public static Fraction operator *(Fraction a, Fraction b) { return new Fraction(a.Numerator * b.Numerator, a.Denominator * b.Denominator); } public static Fraction operator *(Fraction a, int b) { return (new Fraction(a.Numerator * b, a.Denominator)); } public static Fraction operator *(int a, Fraction b) { return (b * a); } #endregion #region operator / public static Fraction operator /(Fraction a, Fraction b) { return new Fraction(a.Numerator * b.Denominator, a.Denominator * b.Numerator); } public static Fraction operator /(Fraction a, int b) { return new Fraction(a.Numerator, a.Denominator * b); } public static Fraction operator /(int a, Fraction b) { return new Fraction(a * b.Denominator, b.Numerator); } #endregion #region operator ++, -- public static Fraction operator ++(Fraction a) { return (a + 1); } public static Fraction operator --(Fraction a) { return (a - 1); } #endregion #region operator == public static bool operator ==(Fraction a, Fraction b) { bool ret = (a.Numerator == b.Numerator); if (a.Numerator != 0) ret &= (a.Denominator == b.Denominator); if (ret && a.Denominator == 0) ret = false; return ret; } public static bool operator ==(Fraction a, int b) { return (a.Denominator == 1 && (a.Numerator == b)); } public static bool operator ==(int a, Fraction b) { return (b == a); } #endregion #region operator != public static bool operator !=(Fraction a, Fraction b) { return !(a == b); } public static bool operator !=(Fraction a, int b) { return !(a == b); } public static bool operator !=(int a, Fraction b) { return !(b == a); } #endregion #region operator < public static bool operator <(Fraction a, Fraction b) { return (a.Numerator * b.Denominator < b.Numerator * a.Denominator); } public static bool operator <(Fraction a, int b) { return (a.Numerator < b * a.Denominator); } public static bool operator <(int a, Fraction b) { return (b.Numerator > a * b.Denominator); } #endregion #region operator > public static bool operator >(Fraction a, Fraction b) { return (a.Numerator * b.Denominator > b.Numerator * a.Denominator); } public static bool operator >(Fraction a, int b) { return (a.Numerator > b * a.Denominator); } public static bool operator >(int a, Fraction b) { return (b.Numerator < a * b.Denominator); } #endregion #region operator <= public static bool operator <=(Fraction a, Fraction b) { return (a.Numerator * b.Denominator <= b.Numerator * a.Denominator); } public static bool operator <=(Fraction a, int b) { return (a.Numerator <= b * a.Denominator); } public static bool operator <=(int a, Fraction b) { return (b.Numerator >= a * b.Denominator); } #endregion #region operator >= public static bool operator >=(Fraction a, Fraction b) { return (a.Numerator * b.Denominator >= b.Numerator * a.Denominator); } public static bool operator >=(Fraction a, int b) { return (a.Numerator >= b * a.Denominator); } public static bool operator >=(int a, Fraction b) { return (b.Numerator <= a * b.Denominator); } #endregion #region operator cast public static explicit operator Fraction(int a) { return new Fraction(a, 1); } public static explicit operator int(Fraction a) { return (int)(a.Numerator / a.Denominator); } public static explicit operator Fraction(decimal a) { return new Fraction(a, 10); } public static explicit operator decimal(Fraction a) { return (a.Numerator / (decimal)a.Denominator); } #endregion #endregion #region constructor public Fraction(int numerator, int denominator) : this() { int gcd = GetGreatestCommonDivisor(numerator, denominator); if (gcd < 0) { gcd = Math.Abs(gcd); numerator = -Math.Abs(numerator); denominator = Math.Abs(denominator); } Numerator = numerator / gcd; Denominator = denominator / gcd; DisplayMultiplier = gcd; } public Fraction(decimal num, int ba) : this() { int de = 1; while (Math.Floor(num) != num && num <= int.MaxValue / ba && num >= int.MinValue / ba && de <= int.MaxValue / ba) { num *= ba; de *= ba; } Numerator = (int)Math.Floor(num); int gcd = GetGreatestCommonDivisor(Numerator, de); if (gcd < 0) { gcd = Math.Abs(gcd); Numerator = -Math.Abs(Numerator); de = Math.Abs(de); } Numerator = Numerator / gcd; Denominator = de / gcd; DisplayMultiplier = gcd; } #endregion #region public method public string ToString(bool bReduce) { int gcd = DisplayMultiplier; if (bReduce || DisplayMultiplier == 0) gcd = 1; string ret; if (Denominator * gcd == 1) { ret = string.Format("{0}", Numerator * gcd); } else { ret = string.Format("{0}/{1}", Numerator * gcd, Denominator * gcd); } return ret; } public override string ToString() { return ToString(true); } public override int GetHashCode() { return base.GetHashCode(); } public override bool Equals(object obj) { bool ret = false; if (obj is Fraction || obj is decimal || obj is int) { ret = (this == (Fraction)obj); } return ret; } public int CompareTo(object obj) { decimal ret; if (obj is Fraction || obj is decimal || obj is int) { ret = (decimal)(this - (Fraction)obj); } else { throw new ArgumentException(); } if (ret < 0) ret--; return (int)Math.Ceiling(ret); } #endregion } } "@}