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

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

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

JavaScript の例(8) (TypeScript)

TypeScript 版の Apply は必要なかったのでまた書き直します。この例の場合は、このようにクロージャーの代わりに文字列を引数とすることができます。一般的にこのようなことができるかどうかはまだ調査しなければなりません.

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

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](xs[0])].concat(LongDecimal.Map(f, xs.slice(1)));
    }

    static *Map2(f: string, xs, y) {
        for (var x of xs) {
            yield Func[f](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](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](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](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 "";
    }
}

class Numbers {
    number: LongDecimal;
    square_difference: LongDecimal;
    scale: number;
    constructor(number: LongDecimal, square_difference: LongDecimal, scale: number) {
        this.number = number;
        this.square_difference = square_difference;
        this.scale = scale;
    }

    GetNextDecimalDigitAndNumbers(): [number, Numbers] {
        var two: LongDecimal = new LongDecimal([2]);
        for (var dd: number = 9; dd >= 0; dd--) {
            var zd: LongDecimal = new LongDecimal([dd]).Shift(this.scale);
            var number_sq_diff: LongDecimal = this.number.Product(zd).Product(two).Sum(zd.Product(zd));
            if (number_sq_diff.LessOrEqual(this.square_difference)) {
                const new_number = this.number.Sum(zd);
                const new_square_difference = this.square_difference.Difference(number_sq_diff);
                return [dd, new Numbers(new_number, new_square_difference, this.scale + 1)];
            }
        }
        return null;
    }
}

class NumbersGenerator {
    GetNextDecimalDigit(): number {
        return generator_gen.next().value;
    }

    *GenerateDecimal(): Iterable<number> {
        var number: LongDecimal = new LongDecimal([]);
        var square_difference: LongDecimal = new LongDecimal([3]);
        var nums: Numbers = new Numbers(number, square_difference, 0);
        for (; ;) {
            var d_ns = nums.GetNextDecimalDigitAndNumbers();
            nums = d_ns[1];
            yield d_ns[0];
        }
    }
}

class NumbersServer {
    current_numbers: Numbers;

    constructor() {
        var number: LongDecimal = new LongDecimal([]);
        var square_difference: LongDecimal = new LongDecimal([3]);
        this.current_numbers = new Numbers(number, square_difference, 0);
    }

    GetNumbers(): Numbers {
        return this.current_numbers;
    }

    SetNumbers(numbers): void {
        this.current_numbers = numbers;
    }

    *GenerateDecimalServer(): Iterable<Numbers> {
        for (; ;) {
            this.current_numbers = yield this.current_numbers;
        }
    }
}

var generator_gen;
var generator_server_gen;

function* UnfoldL(next: string, init) {
    var cpl = Func[next](init);
    // 終了したとき next は null を返すとする
    if (cpl == null) {
        return;
    }
    yield cpl[0];
    // ここは for を使わないと書けない
    for (var e of UnfoldL(next, cpl[1])) {
        yield e;
    }
}

function* Take<T>(n: number, generator: Iterable<T>): Iterable<T> {
    var count = 0;
    for (var d of generator) {
        if (count == n) {
            break;
        }
        count++;
        yield d;
    }
}

class Calc {
    count: number;
    generator: NumbersGenerator;
    generator_server: NumbersServer;
    result_number: LongDecimal;

    constructor() {
        this.count = 21;
        this.generator = new NumbersGenerator();
        this.generator_server = new NumbersServer();
        this.result_number = new LongDecimal([]);
        generator_gen = this.generator.GenerateDecimal();
        generator_server_gen = this.generator_server.GenerateDecimalServer();
    }

    RepeatGenerator(): string {
        var repeat = function (number: LongDecimal, e: number, count: number, generator: NumbersGenerator): LongDecimal {
            if (e < count) {
                return repeat(number.Sum(new LongDecimal([generator.GetNextDecimalDigit()]).Shift(e)), e + 1, count, generator);
            } else {
                return number;
            }
        }
        var result_number: LongDecimal = repeat(new LongDecimal([]), 0, this.count, this.generator);
        return result_number.Print();
    }

    RepeatServer(): string {
        var repeat = function (number: LongDecimal, e: number, count: number, server: NumbersServer): LongDecimal {
            if (e < count) {
                var current_numbers = server.GetNumbers();
                var d_ns = current_numbers.GetNextDecimalDigitAndNumbers();
                var dd = d_ns[0];
                server.SetNumbers(d_ns[1]);
                return repeat(number.Sum(new LongDecimal([dd]).Shift(e)), e + 1, count, server);
            } else {
                return number;
            }
        }
        var result_number: LongDecimal = repeat(new LongDecimal([]), 0, this.count, this.generator_server);
        return result_number.Print();
    }

    IterateGenerator(): string {
        return new LongDecimal(Take(this.count, this.generator.GenerateDecimal())).Print();
    }

    IterateServer(): string {
        var number = new LongDecimal([]);
        var square_difference = new LongDecimal([3]);
        var init_numbers = new Numbers(number, square_difference, 0);
        return new LongDecimal(Take(this.count, UnfoldL("NextDigitAndNumbers", init_numbers))).Print();
    }
}

console.log(new Calc().RepeatGenerator());
console.log(new Calc().RepeatServer());
console.log(new Calc().IterateGenerator());
console.log(new Calc().IterateServer());