先日名古屋で開かれた FLOSS 桜山の第 11 回勉強会で、JavaScript 1.7 で導入されたジェネレータに関する話をしてきました。発表資料などは以下の通りです。
ジェネレータ、配列内包、分割代入は JavaScript 1.7 から、関数の省略表記、ジェネレータ内包、正規表現の先頭固定 (sticky) オプションは JavaScript 1.8 からの対応です。Firefox 2 は JavaScript 1.7 に、Firefox 3 は JavaScript 1.8 に対応していますが、いくつかの機能を使うためには <script type="application/javascript; version=1.7">
のように明示的にバージョン指定をしなくてはいけません。
「Haskell のリストっぽいもの」でやってることは、「無限リストと遅延評価」の「ジェネレータを使った無限リスト」および「JavaScript でカリー化、再び」の続きです。curry 関数を関数オブジェクトのメソッドとし、以下のような cons 関数を使っています。
const cons = function (head, tail) {
yield head;
for (var i in tail)
yield i;
}.curry();
print 関数は、複数の引数を与えるとそれらをスペース区切りで出力する関数です。配列内包との組み合わせは以下のように解釈されます。
var list = cons(1, cons(2, cons(3, nil())));
print.apply(null, [i for (i in list)]);
// ↓
print.apply(null, [1, 2, 3]);
// ↓
print(1, 2, 3)
// => 1 2 3
JavaScript のジェネレータ機能は基本的に Python のそれと同じですが、ジェネレータイテレータオブジェクトの close メソッドの動作が異なります。close メソッドが呼び出されたとき、Python ではジェネレータの実行を再開し、再開地点で GeneratorExit 例外を投げますが、JavaScript ではジェネレータの実行を再開し、再開地点で return 文に出会ったかのようにジェネレータの実行を終了します。また、Pyothon では GeneratorExit、StopIteration 例外が close メソッドの呼び出し元に伝播しませんが、JavaScript ではジェネレータ内で投げられたすべての例外が close メソッドの呼び出し元に伝播します。
# Python 2.5
def gen():
try:
yield
except:
print "in except/catch clause"
finally:
print "in finally clause"
raise StopIteration
g = gen()
g.next()
g.close()
# => in except/catch clause
# => in finally clause
// JavaScript 1.7
function gen() {
try {
yield;
} catch (ex) {
print("in except/catch clause");
} finally {
print("in finally clause");
throw StopIteration;
}
}
var g = gen();
g.next();
g.close();
// => in finally clause
// => uncaught exception: [object StopIteration]
配列内包およびジェネレータ内包の式部分には、関数呼び出しやオブジェクトの生成などを含む任意の式を掛けます。内包表記自体も式なので、入れ子にして使うこともできます。
[[j for (j in enumFromTo(1, i))]
for (i in enumFromTo(1, 4))].toSource();
// => [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
// enumFromTo 関数については後述
配列内包もジェネレータ内包も、for 節を重ねたり if 節を付加したりできます。
セコメントをする