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

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

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

JavaScript の例(6) (TypeScript)

TypeScript 版の LongDecimal クラスを引数にクロージャーを使わないように書き直します。TypeScript ではクロージャーを引数にすることができるのですが、これができないようなプログラミング言語に移植するために書き直してみます。以下のように引数は文字列として、外部で文字列と関数を対応させるようにします。他のクラスも同様となります。

const Func = {
    Comp: {
        Apply(x: number): number {
            return 9 - x;
        }
    },
    Sum: {
        Apply(x: number, y: number): number {
            return x + y;
        }
    },
    SumL: {
        Apply(x: LongDecimal, y: LongDecimal): LongDecimal {
            return x.Sum(y);
        }
    },
    Product: {
        Apply(x: number, y: number): number {
            return x * y;
        }
    },
    Equal: {
        Apply(x: number, y: number): boolean {
            return x == y;
        }
    },
    EqualAndLess: {
        Apply(x: number, y: number): [boolean, boolean] {
            return [x == y, x < y];
        }
    },
    CarryStep: {
        Apply(x: [number, LongDecimal], y: number): [number, LongDecimal] {
            return LongDecimal.CarryStep(x, y);
        }
    },
}

class LongDecimal {
    decimals: Array<number>;
    constructor(numbers: Iterable<number>) {
        this.decimals = [];
        for (var n of numbers) {
            this.decimals.push(n);
        }
    }

    static Map(f: string, xs) {
        if (xs.length == 0) {
            return [];
        }
        return [Func[f].Apply(xs[0])].concat(LongDecimal.Map(f, xs.slice(1)));
    }

    static *Map2(f: string, xs, y) {
        for (var x of xs) {
            yield Func[f].Apply(x, y);
        }
    }

    static Align0<T>(xs: T[], len: number, z: T): T[] {
        var result: T[] = [];
        for (var xd of xs) {
            result.push(xd);
        }
        for (var i = xs.length; i < len; i++) {
            result.push(z);
        }
        return result;
    }

    static ZipWith(f: string, xs, ys) {
        var result = [];
        for (var i = 0; i < xs.length && i < ys.length; i++) {
            result.push(Func[f].Apply(xs[i], ys[i]));
        }
        return result;
    }

    static ZipWith0(z, f: string, xs, ys) {
        var xsx = LongDecimal.Align0(xs, ys.length, z);
        var ysx = LongDecimal.Align0(ys, xs.length, z);
        return LongDecimal.ZipWith(f, xsx, ysx);
    }

    static Replicate<T>(times: number, val: T): T[] {
        var result: T[] = [];
        for (var i = 0; i < times; i++) {
            result.push(val);
        }
        return result;
    }

    static LookUpFirst<T, U>(f: (key: T) => boolean, pairs: [T, U][]): U {
        for (var [key, val] of pairs) {
            if (f(key)) {
                return val;
            }
        }
        // 見つからないときは何も返さない
    }

    static FoldL0(z, f: string, xs) {
        if (xs.length == 0) {
            return z;
        }
        return Func[f].Apply(LongDecimal.FoldL0(z, f, xs.slice(0, xs.length - 1)), xs[xs.length - 1]);
    }

    static FoldR0(z, f: string, xs) {
        if (xs.length == 0) {
            return z;
        }
        return Func[f].Apply(LongDecimal.FoldR0(z, f, xs.slice(1)), xs[0]);
    }

    static CarryStep(carry_and_dec: [number, LongDecimal], x: number): [number, LongDecimal] {
        var [carry, dec]: [number, LongDecimal] = carry_and_dec;
        var newdec = new LongDecimal([(x + carry) % 10].concat(dec.decimals));
        return [Math.floor((x + carry) / 10), newdec];
    }

    Decimalize(c: number, leave: boolean): LongDecimal {
        var [carry, dec]: [number, LongDecimal] = LongDecimal.FoldR0([c, new LongDecimal([])], "CarryStep", this.decimals);
        if (leave && carry != 0) {
            return dec.Sum_1(carry * 10);
        }
        else {
            return dec;
        }
    }

    Shift(e: number): LongDecimal {
        return new LongDecimal(LongDecimal.Replicate(e, 0).concat(this.decimals));
    }

    CollectShiftMult(y: LongDecimal): LongDecimal[] {
        var result: LongDecimal[] = [];
        var e = 0;
        for (var xd of this.decimals) {
            result.push(y.Product_1(xd).Shift(e));
            e++;
        }
        return result;
    }

    static EqualAndLess(x: number, y: number): [boolean, boolean] {
        return [x == y, x < y];
    }

    Sum_1(n: number): LongDecimal {
        return new LongDecimal([this.decimals[0] + n].concat(this.decimals.slice(1)));
    }

    Sum(y: LongDecimal): LongDecimal {
        var x: LongDecimal = this;
        return new LongDecimal(LongDecimal.ZipWith0(0, "Sum", x.decimals, y.decimals)).Decimalize(0, true);
    }

    Difference(y: LongDecimal): LongDecimal {
        var x: LongDecimal = this;
        var yds: number[] = LongDecimal.Align0(y.decimals, x.decimals.length, 0);
        return new LongDecimal(LongDecimal.ZipWith0(0, "Sum", x.decimals, LongDecimal.Map("Comp", yds))).Decimalize(1, false);
    }

    Product_1(xd: number): LongDecimal {
        return new LongDecimal(LongDecimal.Map2("Product", this.decimals, xd));
    }

    Product(y: LongDecimal): LongDecimal {
        var x: LongDecimal = this;
        return LongDecimal.FoldL0(new LongDecimal([]), "SumL", x.CollectShiftMult(y));
    }

    Equal(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        return LongDecimal.ZipWith0(0, "Equal", x.decimals, y.decimals).every(b => b);
    }

    NotEqual(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        return !(x.Equal(y));
    }

    Less(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        if (x.Equal(y)) {
            return false;
        }
        return LongDecimal.LookUpFirst(key => !key, LongDecimal.ZipWith0(0, "EqualAndLess", x.decimals, y.decimals));
    }

    LessOrEqual(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        return x.Less(y) || x.Equal(y);
    }

    Greater(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        return !(x.LessOrEqual(y));
    }

    GreaterOrEqual(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        return !(x.Less(y));
    }
    Print1(n: number): string {
        if (n < this.decimals.length) {
            return String(this.decimals[n]) + this.Print1(n + 1);
        }
        return "";
    }
    Print(): string {
        if (this.decimals.length > 0) {
            return String(this.decimals[0]) + "." + this.Print1(1);
        }
        return "";
    }
}