ES2015 の iterable/iterator/generator による無限 FizzBuzz
ES2015 の iterable protocol / iterator protocol だとそこそこ自然に無限リストを作れるわけなので、ちょっと試しにやってみました。node v5.2.0 で動かしました。
"use strict";
function* countup(n) {
for (;;) yield n++;
}
function* map(iterable, func) {
for (let value of iterable) {
yield func(value);
}
}
function* cycle(iterable) {
for (;;) {
for (let value of iterable) {
yield value;
}
}
}
function take(iterable, n) {
const ret = [];
for (let value of iterable) {
ret.push(value);
if (!(ret.length < n)) break;
}
return ret;
}
function* zip(iterable) {
for (;;) {
const nexts = [];
for (let it of iterable) {
nexts.push(it.next());
}
yield nexts.map( (i) => i.value);
if (nexts.some( (i) => i.done) ) break;
}
}
console.log(
take(
map(
zip([
countup(1),
cycle(["", "", "Fizz"]),
cycle(["", "", "", "", "Buzz"])
]),
(_) => _[1] + _[2] || _[0]
),
30
)
); FizzBuzz の map のところは destructive assignment ができるともうちょい綺麗に書けますが、現時点だとまだオプションが必要なのでキモい書きかたになりました。
気になった点
protocol と言っている通り、next() を適切なインターフェイスで実装しているものは全て iterator なため、イテレータ全般に対して基底クラスみたいなものがありません。これはこれでいいんですが、不便な点があります。イテレータに対してメソッドの追加というのができません。
自分の中のオブジェクト指向の気持ちは以下のように書きたいのです。
[
countup(1),
cycle(["", "", "Fizz"]),
cycle(["", "", "", "", "Buzz"])
].
zip().
map( (_) => _[1] + _[2] || _[0] ).
take(30) しかし、イテレータの prototype というのは存在しないので、毎回必ず何らかの関数形式のラッパーが必要になってしまいます。
関連エントリー
- Generator は iterator であり、iterable でもある 表題の通りですが、Generator にはいずれの protocol も実装されています。気になるのは iterable の挙動ですが、どう...
- Benchmark.js の結果表示を改善する Benchmark.js ちゃんと使えるので良いのですが、計測を頑張っている割に結果表示が貧弱というのが悲しいところです。 なので Perl...
- textarea.value は代入すると値が変わる 以下のような挙動をする。 var textarea = document.createElement('textarea'); textar...
- Perl の Locale::Maketext::Lexicon::Gettext フォーマットのメッセージをJSでフォーマットする Perl の Locale::Maketext::Lexicon::Gettext は以下のような Gettext ライクなフォーマットを扱...