JavaScript のジェネレータ

nanto_vi, 2008-03-19

自己紹介

JavaScript のジェネレータって?

処理のまとまり、すなわち関数

普通の関数
実行時には毎回新たな環境 (実行コンテキスト) が作られる
ジェネレータ
実行環境を維持したまま中断・再開が可能

コルーチンの一種

コルーチン

これからのプログラミング言語には必須機能?

基本的な使い方

function fib() {
  var [a, b] = [1, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

var g = fib();
g.next(); // => 1
g.next(); // => 1
g.next(); // => 2
g.next(); // => 3
g.next(); // => 5

非同期処理を同期処理のように書く

var proc = (function () {
  print("0 秒後");
  // ↓ 10 秒後に再開するよといっておいて
  setTimeout(function () { proc.next(); },
             10000);
  // ↓ とりあえず中断する
  yield;
  print("10 秒後");
})();

proc.next();

省略記法

function () { return expression; }
// ↓
function () expression
(function () {
  for (let i in iterator)
    yield expression;
})()
// ↓
(expression for (i in iterator))

Haskell のリストっぽいもの

下準備

cons 関数
var list = cons(1, cons(2, cons(3, nil())));
list.next(); // => 1
list.next(); // => 2
list.next(); // => 3
list.next();
// => uncaught exception: [object StopIteration]

自然数、偶数

natural = [1..]
even = [2 * n | n <- [1..]]
const natural = function ()
  cons(1, (n + 1 for (n in natural())));

const even = function ()
  (2 * n for (n in natural()));

print.apply(null,
  [i for (i in take(10, even()))]);
// => 2 4 6 8 10 12 14 16 18 20

フィボナッチ数

fib = 1 : 1 : zipWith (+) fib (tail fib)
const fib = function ()
  cons(1,
  cons(1,
  (a + b for ([a, b] in zip(fib(), tail(fib()))))));

print.apply(null,
  [i for (i in take(10, fib()))]);
// => 1 1 2 3 5 8 13 21 34 55

正規表現の先頭固定 (sticky) オプション

Perl 正規表現の \G が常にパターンの先頭についている状態に

function numberList(source) {
  var pattern = /\d+\s*/y;
  var match;
  while ((match = pattern.exec(source)))
    yield +match[0];
  if (pattern.lastIndex != source.length)
    throw "Invalid character";
}

var source = "3 6 9 12 15";
print.apply(null,
  [i for (i in numberList(source))]);
// => 3 6 9 12 15

var source = "3 6 x 12 15";
print.apply(null,
  [i for (i in numberList(source))]);
// => Invalid character

宣伝

WEB+DB PRESS 4 月発売号より「JavaScript + ブラウザ探検 (仮)」連載開始