C# の例(2)
LongDecimal クラスの説明
LongDecimal は小数点以下の任意の桁数まで計算できるクラスです。小数点より上の整数の部分は一桁しか計算できないので、このままでは普通に使うことはできませんが、ここではこれでも大丈夫です。
F#に移植するため、できるだけC#の関数(LINQ to Object)を使うように書き直しています。しかしF#のクラスの書き方がまだよくわからないので移植はまだできていません。
以下でメンバー関数を説明します。
Align0(xs, len, z)
xs の小数点以下に z (ここではゼロ)を追加して桁数を少なくとも len にしたもの。
ZipWith0(z, f, xs, ys)
xs または ys の小数点以下に z (ここではゼロ)を追加して桁数を揃えて各桁に f を適用した結果の小数の桁の列。C#でZipがあるのですが、長さが違うときは短い方に揃えるものなので、ここでは使えないので作成しました。
FoldR0(z, f, xs)
初期値 z に演算 f を右から順にすべての xs の要素に累積して適用した結果。Haskell の foldr を同様のもの。C# の Aggregate 関数は左から順に適用されるので、この関数を作成しました。
CarryStep(carry_and_dec, x)
一桁の繰り上がりを処理した結果の繰り上がりの数とその下位の小数の組。
Sum(n)
小数と整数の和。
Decimalize(c, leave)
小数を正規化したもの。c を最下位の桁の繰り上がりの数として、すべての桁に繰り上がりの処理を行ったものを返します。leave ならば最上位の桁(整数の桁)は処理を行わず返します。
Shift(e)
10 の e 乗分の 1 倍にしたもの。
Product(xd)
小数と整数の積。
CollectShiftMult(y)
一桁ずつの数と小数 y の積の集まり。
operator +(x, y)
x と y の和。各桁の和の小数を正規化したもの。整数部分は正規化しません。
operator -(x, y)
x と y の差。各桁の補数との和の小数に最下位の桁に 1 を加えたものを正規化したもの。整数部分も正規化します。
operator *(x, y)
x と y の積。
EqualAndLess(x, y)
「x と y は等しいか」と「x は y より小さいか」の組。
operator ==(x, y)
x と y は等しい。
operator !=(x, y)
x と y は等しくない。
operator <(x, y)
x は y より小さい。
operator <=(x, y)
x は y より小さいか x と y は等しい。
operator >(LongDecimal x, LongDecimal y)
x は y より大きい。
operator >=(x, y)
x は y より大きいか x と y は等しい。
Sum(y)
「この数」と y の和。
Difference(y)
「この数」と y の差。
Product(y)
「この数」と y の積。
Equal(y)
「この数」と y は等しい。
NotEqual(y)
「この数」と y は等しくない。
Less(y)
「この数」は y より小さい。
LessOrEqual(y)
「この数」は y より小さいか「この数」と y は等しい。
Greater(y)
「この数」は y より大きい。
GreaterOrEqual(y)
「この数」は y より大きいか「この数」と y は等しい。
string Print()
小数を表す文字列。
LongDecimal クラスのコード
internal class LongDecimal { private List<int> decimals; public LongDecimal() { decimals = new List<int>(); } public LongDecimal(int n) { decimals = new List<int>(); decimals.Add(n); } public LongDecimal(int n, int scale) { decimals = new List<int>(); for (int i = 0; i < scale; i++) { decimals.Add(0); } decimals.Add(n); } public LongDecimal(IEnumerable<int> numbers) { decimals = new List<int>(); foreach (int n in numbers) { decimals.Add(n); } } public LongDecimal(int n, IEnumerable<int> numbers) { decimals = new List<int>(); decimals.Add(n); foreach (int x in numbers) { decimals.Add(x); } } public override bool Equals(Object obj) { if ((obj == null) || !this.GetType().Equals(obj.GetType())) { return false; } else { return this == (LongDecimal)obj; } } public override int GetHashCode() { return decimals.GetHashCode(); } private static IEnumerable<T> Align0<T>(IEnumerable<T> xs, int len, T z) { List<T> result = new List<T>(); foreach (T xd in xs) { result.Add(xd); } for (int i = xs.Count(); i < len; i++) { result.Add(z); } return result; } private static IEnumerable<U> ZipWith0<T, U>(T z, Func<T, T, U> f, IEnumerable<T> xs, IEnumerable<T> ys) { IEnumerable<T> xsx = Align0(xs, ys.Count(), z); IEnumerable<T> ysx = Align0(ys, xs.Count(), z); return xsx.Zip(ysx, f); } private static U FoldR0<T, U>(U z, Func<U, T, U> f, IEnumerable<T> xs) { return xs.Reverse().Aggregate(z, f); } private static Tuple<int, LongDecimal> CarryStep(Tuple<int, LongDecimal> carry_and_dec, int x) { int carry = carry_and_dec.Item1; LongDecimal dec = carry_and_dec.Item2; LongDecimal newlist = new LongDecimal((x + carry) % 10, dec.decimals); return new Tuple<int, LongDecimal>((x + carry) / 10, newlist); } private LongDecimal Sum(int n) { return new LongDecimal(decimals.First() + n, decimals.Skip(1)); } private LongDecimal Decimalize(int c, bool leave) { Tuple<int, LongDecimal> initpair = new Tuple<int, LongDecimal>(c, new LongDecimal()); Tuple<int, LongDecimal> respair = FoldR0<int, Tuple<int, LongDecimal>>(initpair, CarryStep, decimals); if (leave && respair.Item1 != 0) { return respair.Item2.Sum(respair.Item1 * 10); } else { return respair.Item2; } } private LongDecimal Shift(int e) { return new LongDecimal(Enumerable.Repeat(0, e).Concat(decimals)); } private LongDecimal Product(int xd) { return new LongDecimal(decimals.Select(yd => xd * yd)); } private IEnumerable<LongDecimal> CollectShiftMult(LongDecimal y) { int e = 0; foreach (int xd in decimals) { yield return y.Product(xd).Shift(e); e++; } } public static LongDecimal operator +(LongDecimal x, LongDecimal y) { return new LongDecimal(ZipWith0(0, (a, b) => a + b, x.decimals, y.decimals)).Decimalize(0, true); } public static LongDecimal operator -(LongDecimal x, LongDecimal y) { IEnumerable<int> yds = Align0(y.decimals, x.decimals.Count(), 0); return new LongDecimal(ZipWith0(0, (a, b) => a + b, x.decimals, yds.Select(a => 9 - a))).Decimalize(1, false); } public static LongDecimal operator *(LongDecimal x, LongDecimal y) { return x.CollectShiftMult(y).Aggregate(new LongDecimal(), (a, b) => a + b); } private static Tuple<bool,bool> EqualAndLess(int x, int y) { return new Tuple<bool, bool>(x == y, x < y); } public static bool operator ==(LongDecimal x, LongDecimal y) { return ZipWith0<int, bool>(0, (a, b) => a == b, x.decimals, y.decimals).All(b => b); } public static bool operator !=(LongDecimal x, LongDecimal y) { return !(x == y); } public static bool operator <(LongDecimal x, LongDecimal y) { if (x == y) { return false; } return ZipWith0<int, Tuple<bool, bool>>(0, EqualAndLess, x.decimals, y.decimals).First(cpl => !cpl.Item1).Item2; } public static bool operator <=(LongDecimal x, LongDecimal y) { return x < y || x == y; } public static bool operator >(LongDecimal x, LongDecimal y) { return !(x <= y); } public static bool operator >=(LongDecimal x, LongDecimal y) { return !(x < y); } public LongDecimal Sum(LongDecimal y) { LongDecimal x = this; return new LongDecimal(ZipWith0(0, (a, b) => a + b, x.decimals, y.decimals)).Decimalize(0, true); } public LongDecimal Difference(LongDecimal y) { LongDecimal x = this; IEnumerable<int> yds = Align0(y.decimals, x.decimals.Count(), 0); return new LongDecimal(ZipWith0(0, (a, b) => a + b, x.decimals, yds.Select(a => 9 - a))).Decimalize(1, false); } public LongDecimal Product(LongDecimal y) { LongDecimal x = this; return x.CollectShiftMult(y).Aggregate(new LongDecimal(), (a, b) => a + b); } public bool Equal(LongDecimal y) { LongDecimal x = this; return ZipWith0<int, bool>(0, (a, b) => a == b, x.decimals, y.decimals).All(b => b); } public bool NotEqual(LongDecimal y) { LongDecimal x = this; return !(x == y); } public bool Less(LongDecimal y) { LongDecimal x = this; if (x == y) { return false; } return ZipWith0<int, Tuple<bool, bool>>(0, EqualAndLess, x.decimals, y.decimals).First(cpl => !cpl.Item1).Item2; } public bool LessOrEqual(LongDecimal y) { LongDecimal x = this; return x.Less(y) || x.Equal(y); } public bool Greater(LongDecimal y) { LongDecimal x = this; return !(x.LessOrEqual(y)); } public bool GreaterOrEqual(LongDecimal y) { LongDecimal x = this; return !(x.Less(y)); } public string Print() { string res = ""; int count = 0; foreach (int d in decimals) { if (count == 1) { res += "."; } res += d; count++; } return res; } } }