C# の例(3)
C#版もコンストラクターなどを書き直したので再びここに公開します。C#版も最初は移植のためにコンストラクターを1個にしようとしていたのですが、非常に見にくくなってしまったのでコンストラクターが増えてしまいました。C#ではリストが [a] のように書けないので難しいです。なんとか3個に削減しました。そのほか移植のためにメソッド名なども変更したものがあります。
internal class Numbers { private LongDecimal number; private LongDecimal square_difference; private int scale; private int current_digit = 0; public Numbers(LongDecimal number, LongDecimal square_difference, int scale) { this.number = number; this.square_difference = square_difference; this.scale = scale; } public int CurrentDigit { get { return current_digit; } } public int GetNextDecimalDigit() { LongDecimal two = new LongDecimal(2); for (int dd = 9; dd >= 0; dd--) { LongDecimal zd = new LongDecimal(dd).Shift(scale); LongDecimal number_sq_diff = number * zd * two + zd * zd; if (number_sq_diff <= square_difference) { number = number + zd; square_difference -= number_sq_diff; scale++; current_digit = dd; return dd; } } return 0; } }
サーバーで無限に計算するバージョン
サーバーで無限に計算していてブラウザーで1桁ずつ取り出す場合のクラスは以下のようになります。
internal class NumbersGenerator { public NumbersGenerator() { generator = GenerateDecimal().GetEnumerator(); } private IEnumerator<int> generator; public int GetNextDecimalDigit() { generator.MoveNext(); return generator.Current; } public IEnumerable<int> GenerateDecimal() { LongDecimal number = new LongDecimal(); LongDecimal square_difference = new LongDecimal(3); Numbers nums = new Numbers(number, square_difference, 0); for(; ;) { yield return nums.GetNextDecimalDigit(); } } }
ブラウザーで計算するバージョン
サーバーにデータがあってそれをブラウザーで取り出して1桁ずつ計算する場合のクラスは以下のようになります。
internal class NumbersServer { public NumbersServer() { generator_server = GenerateDecimalServer().GetEnumerator(); } private IEnumerator<Numbers> generator_server; private Numbers current_numbers; private Numbers GetNumbers() { generator_server.MoveNext(); return generator_server.Current; } private void SetNumbers(Numbers numbers) { current_numbers = numbers; } public Numbers Numbers { get { return GetNumbers(); } set { SetNumbers(value); } } public IEnumerable<Numbers> GenerateDecimalServer() { LongDecimal number = new LongDecimal(); LongDecimal square_difference = new LongDecimal(3); current_numbers = new Numbers(number, square_difference, 0); for (; ; ) { yield return current_numbers; } } }
それぞれの計算をするクラス
internal class Calc { private NumbersGenerator generator; private NumbersServer generator_server; private LongDecimal result_number; private int count = 21; public Calc() { generator = new NumbersGenerator(); generator_server = new NumbersServer(); result_number = new LongDecimal(); } private IEnumerable<T> Iterate<T>(Func<T, T> next, T init) { for (; ; ) { yield return init; init = next(init); } } private Numbers NextNumbers(Numbers numbers) { numbers.GetNextDecimalDigit(); return numbers; } public string RepeatGenerator() { for(int e = 0; e < count; e++) { result_number = result_number + new LongDecimal(generator.GetNextDecimalDigit()).Shift(e); } return result_number.Print(); } public string RepeatServer() { for (int e = 0; e < count; e++) { Numbers numbers = generator_server.Numbers; int dd = numbers.GetNextDecimalDigit(); generator_server.Numbers = numbers; result_number = result_number + new LongDecimal(dd).Shift(e); } return result_number.Print(); } public string IterateGenerator() { return new LongDecimal(generator.GenerateDecimal().Take(count)).Print(); } public string IterateServer() { LongDecimal number = new LongDecimal(); LongDecimal square_difference = new LongDecimal(3); Numbers init_numbers = new Numbers(number, square_difference, 0); init_numbers.GetNextDecimalDigit(); return new LongDecimal(Iterate(NextNumbers, init_numbers).Take(count).Select(numbers => numbers.CurrentDigit)).Print(); } }
- サーバーで無限に計算する場合
- サーバーから1回ずつ値を取り出す方法 (RepeatGenerator)
- イテレーターを直接使う方法 (RepeatServer)
- ブラウザーで計算する場合
- サーバーから1回ずつ値を取り出す方法 (IterateGenerator)
- Iterate 関数を使う方法 (IterateServer)
について実行します。
Console.WriteLine(new Calc().RepeatGenerator()); Console.WriteLine(new Calc().RepeatServer()); Console.WriteLine(new Calc().IterateGenerator()); Console.WriteLine(new Calc().IterateServer());
実行結果
小数点以下20桁を計算した結果は以下のようになります。
1.73205080756887729352
1.73205080756887729352
1.73205080756887729352
1.73205080756887729352
小数を表すクラスのコード
C#で書くと長くなりますが、他のプログラミング言語と比較するため書いておきます。
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(IEnumerable<int> numbers) { decimals = new List<int>(); foreach (int n in numbers) { decimals.Add(n); } } 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(new List<int> { (x + carry) % 10 }.Concat(dec.decimals)); return new Tuple<int, LongDecimal>((x + carry) / 10, newlist); } 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 + respair.Item1 * 10; //return respair.Item2.Sum1(respair.Item1 * 10); } else { return respair.Item2; } } public LongDecimal Shift(int e) { return new LongDecimal(Enumerable.Repeat(0, e).Concat(decimals)); } private IEnumerable<LongDecimal> CollectShiftMult(LongDecimal y) { int e = 0; foreach (int xd in decimals) { yield return (y * xd).Shift(e); //yield return y.Product1(xd).Shift(e); e++; } } public static LongDecimal operator +(LongDecimal x, int n) { return new LongDecimal(new List<int> { x.decimals.First() + n }.Concat(x.decimals.Skip(1))); } 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, int xd) { return new LongDecimal(x.decimals.Select(yd => xd * yd)); } 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); } private LongDecimal Sum1(int n) { return new LongDecimal(new List<int> { decimals.First() + n }.Concat(decimals.Skip(1))); } 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); } private LongDecimal Product1(int xd) { return new LongDecimal(decimals.Select(yd => xd * yd)); } 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; } }