エレファント・ビジュアライザー調査記録

ビジュアルプログラミングで数式の変形を表すことを考えていくブロクです。

関数プログラミングと無限論理多項式(8)

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;
        }
    }