E Tech.

JavaScriptのGeneratorの利用場面

JavaScriptのGeneratorがわからない

何をしているのか、どのような場面で利用すると得なのかがわからない。

何をしているのか

イテレータを簡単に実装できる。また、ジェネレータ内の処理は途中で一旦停止できる。

イテレータ

順番に結果を返却できるオブジェクト。配列や文字列など。for(v of iterable)の形で利用できる。

const array = [1,2,3,4,5] //イテレータ
for (const v of array){
    console.log(v)
}

ジェネレータの利用

1から5までの数字の配列を繰り返したいなあというときは、配列を作ってしまえば問題ない。ジェネレータは繰り返したい数がわかっていないときなどに利用できる。

//ジェネレータ関数
function* gen(from, to){
    while(from<=to){
        yield from++
    }
}

//gがジェネレータ
const g = gen(0, 20) //0, 20といった数字は実行時に決められる。
for (const v of g){
    console.log(v)
}

利用すると嬉しい場面

ただ、これだけだと以下のようにジェネレータを利用しなくても似たことはできる。

function gen(from, to) {
  let array = [];
  for (let i = from; i <= to; i++) {
    array.push(i);
  }
  return array;
}

const g = gen(0, 20);
g.map((e) => console.log(e));

どうやら便利なのは、yieldで処理が一旦停止するというところらしい。
配列だとすべての要素を持たないと次の処理に移行できないが、ジェネレータだと一要素ずつ生成される。1万個の要素を持つ配列を初期化してから扱うか、1万個まで要素を返却する関数を作成して、実行時に各要素を初期化して扱うか(遅延評価)、という違いがある。メモリ効率は良くなる。

また、関心の分離もできる。わかりやすかったので「ジェネレーター関数と仲良くなろう!」から引用させてもらいます。

//1, 1, 2, 3, 5, 8, … と、フィボナッチ数列を出力する。ただし3の倍数と3を含む数字列の場合は前後に “!!!” を付加する。(1000くらいで適当に切り上げる。)

//ジェネレータを利用しない版
//処理を全部ループの中に含めている
let prev = 0
let current = 1
while (current < 1000) {
  if (current % 3 === 0 || current.toString().includes('3')) {
    console.log(`!!! ${current} !!!`)
  } else {
    console.log(`${current}`)
  }
  [prev, current] = [current, prev + current]
}

//ジェネレータを利用する版
function *fibonacci() {
  let prev = 0
  let current = 1
  while (current < 1000) {
    yield current; // 次の行が[]なので、このセミコロンは必須
    [prev, current] = [current, prev + current]
  }
}

for (const n of fibonacci()) {
  if (n % 3 === 0 || n.toString().includes('3')) {
    console.log(`!!! ${n} !!!`)
  } else {
    console.log(`${n}`)
  }
}

ジェネレータを利用しなくても実装できるが、「関数の分割(関心の分離)」と「メモリ効率の追求(遅延評価)」を同時に行いたい場面ではジェネレータを利用する意味はある。処理の分離だけしたい場合は、フィボナッチ数列を全部生成した後で、フィルターすればよい。メモリ効率だけ追求したい場合は、生成と同時にフィルターすればよい。両方を同時に行いたい場合は、ジェネレータで「要素を生成するロジック」だけを切り出して、そこにフィルター処理を適用する。

あと具体的には、APIから取得したデータをフィルタリングしていく場合に利用できる。ここがまだわかっていないのでまた今度に回す。

参考

JavaScript の ジェネレータ を極める!
ジェネレーター関数と仲良くなろう!
JavaScriptのGeneratorを使うとなぜ関数型プログラミングが捗るのか調べてみた