トランポリン化で再帰関数のスタックオーバーフローを回避

function trampoline(f) {
    return (...args) => {
        let result = f(...args);
        while (typeof result === 'function') {
            result = result();
        }
        return result;
    };
}
function factTramp(n, accumulator = 1) {
    if (n <= 0) return accumulator;
    return () => factTramp(n - 1, n * accumulator);
}
/**
 * 表か裏が均等な確率で出るコインを 2n回 投げる。
 * 表と裏がそれぞれ同じ回数、つまり n回 出る確率は?
 */
function* 表と裏が同数出る確率の列挙(n = 1) {
    const factTable = [];
    let i = 0;
    while (i++ < n) {
        if (factTable[2 * i] == undefined) factTable[2 * i] = trampoline(factTramp)(2 * i);
        if (factTable[i] == undefined) factTable[i] = trampoline(factTramp)(i);
        yield [factTable[2 * i] / Math.pow(factTable[i], 2), Math.pow(2, 2 * i)];
    }
}
 
function gcdTramp(a, b) {
    if (isNaN(a) || isNaN(b) || a === Infinity || b === Infinity) return NaN;
    return b === 0
        ? a
        : () => gcdTramp(b, a % b);
}
 
for (const probability of 表と裏が同数出る確率の列挙(100)) {
    const gcd = trampoline(gcdTramp)(probability[0], probability[1]);
    document.querySelector('main').innerHTML += `<br>
        ${probability[0] / gcd} / ${probability[1] / gcd}<br>
        (${probability[0] / probability[1]})<br>
    `;
}