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

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

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

JavaScript の例(2) (TypeScript)

この記事の目的は、サーバーで無限に実行されているプログラムとブラウザーで実行されるプログラムの組み合わせがあるとき、ブラウザー側のプログラムを更新することは、サーバー側のプログラムを同じように更新することと同じことになるということを示すことです。それを F# などのプログラミング言語を使って示そうと考えています。

この目的のためにはこの項の説明は必要ないのですが、F# に移植するときに必要となるために書いておきます。

このクラスだけではあまり役に立たないのですが、このクラスで表すことができる数の 10 の e 乗倍で任意の10進数を表すことができます。(これを BigDecimal とすると BigDecimal = LongDecimal × int) また 10 を n に変えると任意の n 進数を表すことができます。

LongDecimal クラスのコード

class LongDecimal {
    decimals: Array<number>;
    constructor();
    constructor(n: number);
    constructor(n: number, scale: number);
    constructor(n: Iterable<number>);
    constructor(n: number, ds: number[]);
    constructor(n?: number | Iterable<number>, scale?: number | number[]) {
        this.decimals = [];
        if (typeof n !== "undefined") {
            if (typeof n == "number") {
                if (typeof scale !== "undefined") {
                    if (typeof scale == "number") {
                        for (var i: number = 0; i < scale; i++) {
                            this.decimals.push(0);
                        }
                        this.decimals.push(n);
                    } else {
                        this.decimals = [n].concat(scale);
                    }
                } else {
                    this.decimals.push(n);
                }
            } else {
                for (var e of n) {
                    this.decimals.push(e);
                }
            }
        }
    }

    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<T, U>(f: (x: T, y: T) => U, xs: T[], ys: T[]): U[] {
        var result: U[] = [];
        for (var i = 0; i < xs.length && i < ys.length; i++) {
            result.push(f(xs[i], ys[i]));
        }
        return result;
    }

    static ZipWith0<T, U>(z: T, f: (x: T, y: T) => U, xs: T[], ys: T[]): U[] {
        var xsx: T[] = LongDecimal.Align0(xs, ys.length, z);
        var ysx: T[] = LongDecimal.Align0(ys, xs.length, z);
        return LongDecimal.ZipWith(f, xsx, ysx);
    }

    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, dec.decimals);
        return [Math.floor((x + carry) / 10), newdec];
    }

    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;
            }
        }
        // 見つからないときは何も返さない
    }

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

    Decimalize(c: number, leave: boolean): LongDecimal {
        var [carry, dec]: [number, LongDecimal] = this.decimals.reduceRight(LongDecimal.CarryStep, [c, new LongDecimal()]);
        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));
    }

    Product_1(xd: number): LongDecimal {
        return new LongDecimal(this.decimals.map(yd => xd * yd));
    }

    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(y: LongDecimal): LongDecimal {
        var x: LongDecimal = this;
        return new LongDecimal(LongDecimal.ZipWith0(0, (a, b) => a + b, 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, (a, b) => a + b, x.decimals, yds.map(a => 9 - a))).Decimalize(1, false);
    }

    Product(y: LongDecimal): LongDecimal {
        var x: LongDecimal = this;
        return x.CollectShiftMult(y).reduce((a, b) => a.Sum(b), new LongDecimal());
    }

    Equal(y: LongDecimal): boolean {
        var x: LongDecimal = this;
        return LongDecimal.ZipWith0(0, (a, b) => a == b, 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, LongDecimal.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));
    }
    Print(): string {
        var res: string = "";
        var count: number = 0;
        for (var d of this.decimals) {
            if (count == 1) {
                res += ".";
            }
            res += d;
            count++;
        }
        return res;
    }
}