Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

Generatorで自然数列とか素数列を作った

「ゼッタイにこういうライブラリあるだろ」と思って探したんですが、なかったのでここに書いておきます。ライブラリ化はするまでもないですが、たびたび使うので…

// 自然数列. [0, 1, 2, 3, 4, ...]. 数列の最初の数を引数として受け取ります.
const naturalNumbers = function*(n = 0) {
    while (true)
        yield n++;
};
// フィボナッチ数列. [0, 1, 1, 2, 3, 5, ...]. はじめの2項を受け取ることもできます
const fibonacciNumbers = function*(n = 0, m = 1) {
    while (true) {
        yield n;
        [n, m] = [m, n + m];
    }
};
// 素数列. [2, 3, 5, 7, 11, ...]. キャッシュ機構がついているので高速に回るはずです.
const primeNumbers = (() => {
    const _primeNumbers = ((cache = [2,3,5,7,11,13,17,19,23,29]) => function*() {
        yield* cache;
        for (let i = cache[cache.length - 1] + 2; ; i += 2) {
            let isPrime = true;
            for (const j of cache) {
                if (j * j > i)
                    break;
                if (i % j == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
                cache.push(yield i);
        }
    });
    return _primeNumbers();
})();
// 乱数列. [29, 74, 51, 62, ...] のように、引数として受け取った数の範囲の乱数を生成します.デフォルトは0〜99.
const randomNumbers = function*(n = 100) {
    while (true)
        yield 0 | Math.random() * n;
};
// 降下列. [10, 9, 8, 7, ...] のように、引数として受け取った値から1つずつ下がっていきます.
const downFrom = function*(n) {
    while (n >= 0)
        yield n--;
};
// リピート. [0, 0, 0, 0, ...] のように、受け取った値を繰り返し続けます
const repeat = function*(n) {
    while (true)
        yield n;
};

使用例

このように使います

// 自然数を順番に取り出したい
for (const n of naturalNumbers()) {
    console.log(n);
}

ついでにいくつか補助関数をつくりました。組み合わせると便利なはずです。

const pair = function*(arr1, arr2) {
    const item2 = () => arr2.next().value;
    for (const item of arr1)
        yield [item, item2()];
};
const map = function*(arr, f) {
    for (const item of arr)
        yield f(item);
};
const take = (gen, n) => {
    const res = [];
    for (const item of gen) {
        if (n-- <= 0)
            break;
        res.push(item);
    }
    return res;
};

補助関数の使用例:

for (const [index, prime] of pair(naturalNumbers(1), primeNumbers())) {
    console.log(`${index}番目の素数は${prime}`);
}
// 小さいほうから素数を10個取り出す
console.log(take(primeNumbers(), 10));