delayed list

2008.7.10

前回紹介した反復処理ライブラリは、mapやfilterなどの一部のメソッドで遅延リストを返すようにして部分的にストリームのパラダイムを取り入れていました。要素は要求に応じて計算されるまで存在しないのにも関わらず、利用者からはあたかもリスト全体が存在しているかのように見え、リスト演算でプログラムを表すことができます。

例えば、n番目の素数を見つけるプログラムは:

function isPrime(n:uint):Boolean {
  function smallestDivisor(n:uint):uint {
    return Iterable
      .range(2, Math.sqrt(n) + 1) //2から√nまでの数のうち
      .find(function(divisor:uint):Boolean { //nを割り切れる数が最小除数
        return n % divisor == 0;
      }) || n; //それがなければnが最小除数
  }
  return n == smallestDivisor(n); //最小除数が自身であれば素数
}

function primeAt(n:uint):uint {
  return Iterable
    .count(2) //2以上の整数のうち
    .filter(isPrime) //素数であるものの
    .slice(n - 1, n).toArray()[0]; //n番目
}

trace(primeAt(1000)); // => 7919

とストレートに書けます。toArrayのタイミングで評価が強制され、countからは必要な数だけ整数が供給されます。これがもし遅延リストでなく、通常の配列ならどうでしょうか?仮に「100以下の素数を求めるプログラム」という問題なら以下のようにすればいいだけです。

//符号なし整数の区間を配列で返す
function range(a:uint, b:uint):Array {
  var ary:Array = [];
  for (var i:uint = a; i <= b; ++i) ary.push(i);
  return ary;
}

range(2, 100).filter(function(e:uint, ...rest):Boolean { //引数を合わせるためにラップ
  return isPrime(e);
});

ところが、「素数のn番目を求める」プログラムを配列の演算で表すには、事前にいくつまでの整数を生成したら十分か見積もれない、正確に見積もれたとしたらそれこそが欲している答えになってしまいますから無理があります。

遅延リストを導入することでリストが利用できる機会が広がって楽しくなります。
# AS3の記事ではありますが、半ばSICPの勉強メモとなってしまいました。。