Windows PowerShellでProject Euler(6) ジェネレータ

Problem 2

秀丸でマクロを組んでいて、走らせるとコマンドプロンプトを立ち上げてそのマクロを走らせたファイルのディレクトリに移動するようにしているのですが、そこでpowershellと打つと、スタートメニューからPowerShellを立ち上げるより楽ですね。

入力処理メソッド

次のようにするとawkのような処理ができます。

function sum() {
    begin   { $s = 0 }
    process { $s += $_ }
    end     { $s }
}

1..3 | sum      # 6

関数sumはパイプラインから引数を一つずつ受け取って、processで処理します。しかし、最初だけbeginの部分を、最後だけendの部分を処理します。上はbeginで$sを初期化、endで$sを出力しています。

ジェネレータ

関数が複数の値を返すとジェネレータのような働きをします。

function g() {
    1
    2
    3
}

function sum() {
    begin   { $s = 0 }
    process { $s += $_ }
    end     { $s }
}

g | sum         # 6

この動作を確認してみましょう。実は、PowerShellには簡単な統合開発環境が用意されています。スタートメニューのWindows PowerShellにはサブメニューがあり、そこにWindows PowerShell ISEというアイテムがあります。ステップ実行をすると、g()とsum()を行ったり来たりするのがわかります。すなわち、gは1, 2, 3と値を一つずつ排出してその都度sumを実行します。パイプを使うと、Unixのようなバッファを使った疑似的なものでなく、本当に遅延評価をします。

さて、この機能を使ってフィボナッチ数列を排出するジェネレータを作りましょう。

function fib($limit) {
    $a, $b = 1, 1
    while($a -le $limit) {
        $a
        $a, $b = $b, ($a + $b)
    }
}

(fib 4000000 | where { $_ % 2 -eq 0 } | measure -sum).sum

あまりよくわかっていませんが、

        $a, $b = $b, ($a + $b)

これで、変数を値にまとめて代入できます。上の例ではかっこが必要なので注意。

上のfibは自分自身で打ち切っていますが、本来なら無限列を用意してtakewhileで打ち切りたいところですよね。しかし、これを考えるにはまだ道具立てが足りなすぎるので、あとに回します。