JavaScript のジェネレーターでフィボナッチ数列を返す関数作ってみた
はじめに
今回は, ジェネレーター関数を使って指定した個数だけフィボナッチ数列を返す関数を作ってみました.
ジェネレーターとは?
ジェネレーターとは, ES6 から追加された記法で処理を途中で中断し結果を得たり, 中断したところから処理を再開したりといったことができる仕組みです.
function*
という形で定義することができ, yield
で値を返し処理を中断することができます.
function* generator() {
yield 'hoge';
yield 'foo';
yield 'bar';
}
使う側は, 定義したジェネレーターからジェネレーターオブジェクトを生成し, next()
メソッドを使って処理を進めることができます.
処理が最後まで終わると done が true になります.
var g = generator();
console.log(g.next()); // {value: "hoge", done: false}
console.log(g.next()); // {value: "foo", done: false}
console.log(g.next()); // {value: "bar", done: false}
console.log(g.next()); // {value: undefined, done: true}
iterator と互換性があるので for ~ of を使って値を取り出すこともできます.
こっちのほうだと done が true になるとループを抜けるのでちょっとスマートですね.
var g = generator();
var arr = [];
for (var i of g) {
arr.push(i);
}
console.log(arr); // ["hoge", "foo", "bar"]
サンプルプログラム
フィボナッチ数列を返すジェネレーター関数のサンプルです!
ジェネレータを生成する際に渡した数の分だけフィボナッチ数を返すようになっています.
コード
// フィボナッチ数を返すジェネレーター
function* fibonacci(n) {
var prev = 1;
--n;
yield prev;
var current = 1;
--n;
yield current;
for (var i=0; i<n; ++i) {
// 前の値と今の値を足した数を current に, 今の値を prev に代入
[current, prev] = [prev + current, current];
yield current;
}
}
window.onload = function() {
var f = fibonacci(10);
var sequence = [];
// for of でジェネレーターの結果を取得
for (var i of f) {
sequence.push( i );
}
console.log(sequence.join(',')); // 1,1,2,3,5,8,13,21,34,55
};
解説
ジェネレーター関数を定義
function*
でジェネレーター関数を定義しています.
もちろん普通に引数も受け取れます.
function* fibonacci(n) {
...
}
フィボナッチ数を返す
最初の1回目と, 2回目は 1, 1 を返すためにループ前に yield を実行しています.
var prev = 1;
--n;
yield prev;
var current = 1;
--n;
yield current;
次にループさせながら前後の数を足した値を yield で返すようにしています.
for (var i=0; i<n; ++i) {
// 前の値と今の値を足した数を current に, 今の値を prev に代入
[current, prev] = [prev + current, current];
yield current;
}
n 個分 yield で値を返したら抜けるようになっています.
実際にジェネレーターを生成して使ってみる
↑で定義したジェネレーターを生成し, それを for ~ of で回しながら値を sequence に追加しています.
最後に console.log で表示すればちゃんとフィボナッチ数列が出来上がっているのが分かると思います.
var f = fibonacci(10);
var sequence = [];
// for of でジェネレーターの結果を取得
for (var i of f) {
sequence.push( i );
}
console.log(sequence.join(',')); // 1,1,2,3,5,8,13,21,34,55
おわりに
今回は, ジェネレーターの紹介にフィボナッチ数列を例に使いましたが, もっと色々と便利な活用方法があると思います.
こんな便利な使い方あるよーってのがあればぜひコメント等で教えてもらえると嬉しいです!
あと, ジェネレーターって現場で使ったことほとんどないんですが, それってそもそも自分の引き出しにないからなんですよね.
勉強不足を反省しつつ, 色々とキャッチアップしながら最善のコードが書けるよう学んでいけたらと思います.
Reference
Discussion
私も現場でジェネレータを使ったことないです。
メモリを事前確保することができる場合は単なる配列でよいので
ジェネレーターを使う場面はほとんどない気がしています。