めぐの JavaScript 日記

あーもんど

配列の組み合わせを列挙する

配列の組み合わせを列挙します。

組み合わせ

コード

const combs = function* (arr, rs, repetition = false) {
    rs = [rs].flat();
    for (const r of rs) yield* function* c(current, i, j) {
        if (i == r) {
            yield [...current];
            return;
        } else if (j >= arr.length) return;
 
        current[i] = arr[j];
 
        yield* c(current, i + 1, j + !repetition);
        yield* c(current, i, j + 1);
    }(new Array(r), 0, 0);
};

使用例

const arr = [1, 2, 3, 4, 5];
[...combs(arr, 3)];
/*  [[1, 2, 3], [1, 2, 4],
     [1, 2, 5], [1, 3, 4],
     [1, 3, 5], [1, 4, 5],
     [2, 3, 4], [2, 3, 5],
     [2, 4, 5], [3, 4, 5]]  */


重複組み合わせ

コード

const rCombs = function* (arr, rs) {
    yield* combs(arr, rs, true);
};

使用例

const arr = [1, 2, 3, 4, 5];
[...rCombs(arr, 2)];
/*  [[1, 1, 1], [1, 1, 2], [1, 1, 3],
     [1, 1, 4], [1, 1, 5], [1, 2, 2],
     [1, 2, 3], [1, 2, 4], [1, 2, 5],
     [1, 3, 3], [1, 3, 4], [1, 3, 5],
     [1, 4, 4], [1, 4, 5], [1, 5, 5],
     [2, 2, 2], [2, 2, 3], [2, 2, 4],
     [2, 2, 5], [2, 3, 3], [2, 3, 4],
     [2, 3, 5], [2, 4, 4], [2, 4, 5],
     [2, 5, 5], [3, 3, 3], [3, 3, 4],
     [3, 3, 5], [3, 4, 4], [3, 4, 5],
     [3, 5, 5], [4, 4, 4], [4, 4, 5],
     [4, 5, 5], [5, 5, 5]]            */


日記 (おまけ)

煮物作りすぎた (n回目)

配列の順列を列挙する

配列の順列を列挙します。

順列

コード

const perms = function* (arr, rs, repetition = false) {
    rs = [rs].flat();
    for (const r of rs) yield* function* p(current, i) {
        if (i == 1) for (const e of current) {
            yield [e];
        } else for (let j = 0; j < current.length; ++j) {
            const sub = current.filter((_, k) => repetition || j != k);
            for (const e of p(sub, i - 1)) yield [current[j], ...e];
        }
    }(arr, r);
};

なんかもっといいアルゴリズムある気がする (絶対ある

使用例

const arr = [1, 2, 3];
[...perms(arr, 2)];  // [[1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]]


重複順列

コード

const rPerms = function* (arr, rs) {
    yield* perms(arr, rs, true);
};

使用例

const arr = [1, 2, 3];
[...rPerms(arr, 2)];
/*  [[1, 1, 1], [1, 1, 2], [1, 1, 3],
     [1, 2, 1], [1, 2, 2], [1, 2, 3],
     [1, 3, 1], [1, 3, 2], [1, 3, 3],
     [2, 1, 1], [2, 1, 2], [2, 1, 3],
     [2, 2, 1], [2, 2, 2], [2, 2, 3],
     [2, 3, 1], [2, 3, 2], [2, 3, 3],
     [3, 1, 1], [3, 1, 2], [3, 1, 3],
     [3, 2, 1], [3, 2, 2], [3, 2, 3],
     [3, 3, 1], [3, 3, 2], [3, 3, 3]]  */


日記 (おまけ)

お昼に友達とそうめん作った。レポートおわらん

Python の zip(),map() っぽいやつを作る

Pythonzip()map() っぽいやつを作ります。

Python 風 zip と map

コード

const zip = function* (...args) {
    const end = Math.min(...args.map(x => x.length));
    for (let i = 0; i < end; ++i) yield args.map(x => x[i]);
};
 
const map = function* (func, ...args) {
    for (const e of zip(...args)) yield func(...e);
};

Array.prototype.map() に似ていますが,Pythonmap() は引数に複数の配列を指定できます。

使用例

const arr = [1, 2, 3];
 
const z = zip(arr, arr, arr);
const m = map((a, b) => a * b, arr, arr);
 
for (const e of z) console.log(e);
/*  [1, 1, 1]
    [2, 2, 2]
    [3, 3, 3]  */
 
for (const e of m) console.log(e);
/*  1 4 9  */


日記 (おまけ)

枕と布団干した。明日は友達と課題やったりする。

Python の filter() っぽいやつを作る

Pythonfilter() っぽいやつを作ります。JS の Array.prototype.filter() に似ていますが,一度に全要素を処理しません (多分

Python 風 filter

配列とかを指定した条件でフィルタリングできるやつです。

コード

const filter = function* (func, arr) {
    for (const e of arr) if (func(e)) yield e;
};

使用例

const arr = [5, -1, 4, 3, -7, 9, 1, 7];
const fil = filter(x => x > 0, arr);
 
for (let i = 0; i < 3; ++i) {
    console.log(fil.next().value);  // 5 4 3
}
 
console.log([...fil]);  // [9, 1, 7]

途中で処理をやめて残りを配列に入れるみたいな使い方が考えられますがそれ以外でわざわざ作る理由はなさそうです。

日記 (おまけ)

今日は晴れてたのに夜いきなり雨降りましたね。雨と言えば今日煮物作りすぎたけど論文も書かなきゃいけない (支離滅裂)

Python の enumerate() っぽいやつを作る

Pythonenumerate() っぽいやつを作ります。

Python 風 enumerate

配列の中身と一緒に添字を返してくれるやつです。任意の数を添字の起点にできます。

コード

const enumerate = function* (arr, start = 0) {
    for (const e of arr) yield [start++, e];
};

使用例

const arr = [5, -1, 4, 3, -7, 9, 1, 7];
 
for (const [i, e] of enumerate(arr)) {
    console.log(i, e);
}
 
/*  0 5
    1 -1
    2 4
    3 3
    4 -7
    5 9
    6 1
    7 7  */

わざわざこれを作って使う利点ってなんでしょうね。for 文のカウンタ変数を const にできることくらいでしょうか。
あとは個人的なこだわりか好みかな。

日記 (おまけ)

ミッフィーのぬいぐるみかわいい

Python の range() っぽいやつを作る

既出かと思いますが Pythonrange() っぽいやつを作ります。ぽいやつです。型判定もしなければ例外も投げません。

Python 風 range

コード

const range = function* (...args) {
    let start = Math.floor(args[1] && args[0]) || 0;
    const stop = Math.floor(args[1] ?? args[0]);
    const step = Math.floor(args[2]) || 1;
    const comp = step > 0 ? (a, b) => a < b : (a, b) => a > b;
    for (; comp(start, stop); start += step) yield start;
};

使用例

stop のみを指定する

for (let i of range(10)) {
    console.log(i);
}
 
/*  0
    1
    2
    3
    4
    5
    6
    7
    8
    9  */

start, stop を指定する

console.log([...range(2, 5)]);  // [2, 3, 4]

start, stop, step を指定する

console.log(...range(13, 3, -2));  // 13 11 9 7 5


日記 (おまけ)

フローリング全部掃除した。

分数を表現する ③ 加減・冪乗・少数化

前回に続いて分数の加減と冪乗を実装します。プリミティブ型への変換も。

加減・冪乗・少数化

コード

class Frac {
 
    /* 省略 */
 
    /** @type {function(...(number | Frac)): Frac} */
    add(...other) {
        const [l, r] = [this.reduce(), new Frac(...other).reduce()];
        const d = Frac.lcm(l.d, r.d);
        return new Frac(Math.round(d / l.d * l.n + d / r.d * r.n), d).reduce();
    }
 
    /** @type {function(...(number | Frac)): Frac} */
    sub(...other) {
        return this.add(new Frac(...other).mul(-1));
    }
 
    /** @type {function(number): Frac} */
    pow(pow) {
        const x = this.reduce();
        return new Frac(x.n ** pow, x.d ** pow);
    }
 
    /** @type {function(): number} */
    valueOf() {
        const x = this.reduce();
        return x.n / x.d;
    }
}

使用例

const frac = (n, d) => new Frac(n, d);
 
const x = frac(1, 2);  // (1 / 2)
const y = frac(1, 5);  // (1 / 5)
 
x.add(y);  // (7 / 10)
x.sub(y);  // (3 / 10)
x.pow(2);  // (1 / 4)
 
x * y;  // 0.1
x > y;  // true

Object.prototype.valueOf() をオーバーライドしているので演算子が使えます。ただしプリミティブの Number 型 (浮動小数点数) に変換される。

console.log() とかで分数表示させるには toString() を呼び出すかテンプレートリテラルを使うなどして明示的な文字列化が必要。

日記 (おまけ)

久しぶりに大学の別キャンパスに行って先生に挨拶してきた (一人で行くほどの行動力がないため友達について行くことで達成)。部屋の掃除もした。