C# の例(12)
続いて LongDecimal クラスについても考えていきます。変数とコンストラクターは以下のようにします。変更不可能なリスト ImmutableList を使います。Select の引数にラムダ式を使っていますが、これは型のチェックを避けるためなので使っても良いことにします。
private readonly ImmutableList<int> decimals; public LongDecimal() { decimals = ImmutableList<int>.Empty; } public LongDecimal(int n) { decimals = ImmutableList<int>.Empty.Add(n); } public LongDecimal(IEnumerable<int> numbers) { decimals = numbers.ToImmutableList(); } public LongDecimal(IEnumerable<FuncArg> numbers) { decimals = numbers.Select(x => x.intValue).ToImmutableList(); }
型のチェックを避けるために関数の引数と戻り値の型を表すクラスを定義します。
public class FuncArg { public int intValue; public LongDecimal LongDecimalValue; public bool boolValue; public Tuple<bool, bool> boolboolValue; public Tuple<int, LongDecimal> intLongDecimalValue; public FuncArg(int intValue) { this.intValue = intValue; } public FuncArg(LongDecimal LongDecimalValue) { this.LongDecimalValue = LongDecimalValue; } public FuncArg(bool boolValue) { this.boolValue = boolValue; } public FuncArg(Tuple<bool, bool> boolboolValue) { this.boolboolValue = boolboolValue; } public FuncArg(Tuple<int, LongDecimal> intLongDecimalValue) { this.intLongDecimalValue = intLongDecimalValue; } }
前回と同様に関数を表すクラスを定義します。
private abstract class UFunc { public abstract int Apply(int x); public class DInt { public class Comp : UFunc { public override int Apply(int x) { return 9 - x; } } } } private abstract class BFunc { public abstract FuncArg Apply(FuncArg x, FuncArg y); public class DInt { public class Sum : BFunc { public override FuncArg Apply(FuncArg x, FuncArg y) { return new FuncArg(x.intValue + y.intValue); } } public class Product : BFunc { public override FuncArg Apply(FuncArg x, FuncArg y) { return new FuncArg(x.intValue * y.intValue); } } public class Equal : BFunc { public override FuncArg Apply(FuncArg x, FuncArg y) { return new FuncArg(x.intValue == y.intValue); } } public class EqualAndLess : BFunc { public override FuncArg Apply(FuncArg x, FuncArg y) { return new FuncArg(new Tuple<bool, bool>(x.intValue == y.intValue, x.intValue < y.intValue)); } } } public class DLondDecimal { public class Sum : BFunc { public override FuncArg Apply(FuncArg x, FuncArg y) { return new FuncArg(x.LongDecimalValue + y.LongDecimalValue); } } } public class DIntLondDecimalInt { public class CarryStep : BFunc { public override FuncArg Apply(FuncArg x, FuncArg y) { return new FuncArg(CarryStep(x.intLongDecimalValue, y.intValue)); } } } }
以下の関数を作ります。下の方の関数は、引数の中で外部の変数を参照している場合のためのもので、外部の変数を変更することはできないということにすると参照する変数の数だけ引数を追加すれば良いです。
private static IEnumerable<int> Map(UFunc f, IEnumerable<int> ns) { foreach (int x in ns) { yield return f.Apply(x); } } private static IEnumerable<FuncArg> Map(BFunc f, IEnumerable<int> ns, int y) { foreach (int x in ns) { yield return f.Apply(new FuncArg(x), new FuncArg(y)); } }
以下の関数の引数の型を上記で定義したクラスに変更します。
private static IEnumerable<FuncArg> ZipWith0(int z, BFunc f, IEnumerable<int> xs, IEnumerable<int> ys) { int count = Math.Max(xs.Count(), ys.Count()); IEnumerable<int> xs_x = Align0(xs, z, count); IEnumerable<int> ys_x = Align0(ys, z, count); for (int i = 0; i < count; i++) { int x = xs_x.ElementAt(i); int y = ys_x.ElementAt(i); yield return f.Apply(new FuncArg(x), new FuncArg(y)); } }
以下の関数も、引数の型を上記で定義したクラスに変更します。FoldL0 は新しく作成します。
private static FuncArg FoldL0(FuncArg z, BFunc f, IEnumerable<FuncArg> xs) { FuncArg res = z; foreach(FuncArg x in xs) { res = f.Apply(res, x); } return res; } private static LongDecimal FoldL0(LongDecimal z, BFunc f, IEnumerable<LongDecimal> xs) { return FoldL0(new FuncArg(z), f, xs.Select(x => new FuncArg(x))).LongDecimalValue; } private static FuncArg FoldR0(FuncArg z, BFunc f, IEnumerable<FuncArg> xs) { return FoldL0(z, f, xs.Reverse()); } private static Tuple<int, LongDecimal> FoldR0(Tuple<int, LongDecimal> z, BFunc f, IEnumerable<int> xs) { return FoldR0(new FuncArg(z), f, xs.Select(x => new FuncArg(x))).intLongDecimalValue; }
以下の関数を作成します。
private static bool All(IEnumerable<FuncArg> s) { foreach (FuncArg r in s) { if (!r.boolValue) { return false; } } return true; } private static Tuple<bool, bool> FirstNotItem1(IEnumerable<FuncArg> s) { foreach (FuncArg r in s) { Tuple<bool, bool> bb = r.boolboolValue; if (!bb.Item1) { return bb; } } return null; }
これらを使って、以下の関数を上記の関数を呼び出すよう修正します。
private LongDecimal Decimalize(int c, bool leave) { Tuple<int, LongDecimal> initpair = new Tuple<int, LongDecimal>(c, new LongDecimal()); Tuple<int, LongDecimal> respair = FoldR0(initpair, new BFunc.DIntLondDecimalInt.CarryStep(), decimals); if (leave && respair.Item1 != 0) { return respair.Item2 + respair.Item1 * 10; } else { return respair.Item2; } }
public static LongDecimal operator +(LongDecimal x, LongDecimal y) { return new LongDecimal(ZipWith0(0, new BFunc.DInt.Sum(), x.decimals, y.decimals)).Decimalize(0, true); } public static LongDecimal operator -(LongDecimal x, LongDecimal y) { IEnumerable<int> yds = Align0(y.decimals, 0, x.decimals.Count()); return new LongDecimal(ZipWith0(0, new BFunc.DInt.Sum(), x.decimals, Map(new UFunc.DInt.Comp(), yds))).Decimalize(1, false); } public static LongDecimal operator *(LongDecimal x, int xd) { return new LongDecimal(Map(new BFunc.DInt.Product(), x.decimals, xd)); } public static LongDecimal operator *(LongDecimal x, LongDecimal y) { return FoldL0(new LongDecimal(), new BFunc.DLondDecimal.Sum(), x.CollectShiftMult(y)); } public static bool operator ==(LongDecimal x, LongDecimal y) { return All(ZipWith0(0, new BFunc.DInt.Equal(), x.decimals, y.decimals)); } public static bool operator <(LongDecimal x, LongDecimal y) { if (x == y) { return false; } return FirstNotItem1(ZipWith0(0, new BFunc.DInt.EqualAndLess(), x.decimals, y.decimals)).Item2; }