2012年5月29日火曜日

Powershellクロージャあり〼。その1

PowerShell覚え書き


お題

PowerShell2.0からクロージャ機能があるとのことで、アキュムレータを作ってみました。

function Accum([int]$init)
{
    $i=$init
    { $script:i++; $i}.GetNewClosure()
#    return { $script:i++; return $i}.GetNewClosure() #ちゃんとreturnをつけるとこうなる。
}

"`$i=" + $i #スクリプトスコープ

"Get Accumlator init=100"
$a=Accum 100

"call by `&"
1..3|%{ &$a }

"`$i=" + $i #スクリプトスコープ

"set `$i = 1000"
$i=1000
"call by `&"
1..3|%{ &$a }

"call by `."
1..3|%{ . $a }

"`$i=" + $i #スクリプトスコープ


実行結果は以下の通り。
    $i=
    Get Accumlator init=100
    call by &
    101
    102
    103
    $i=
    set $i = 1000
    call by &
    104
    105
    106
    call by .
    107
    108
    109
    $i=1000


メモ

  • クロージャ作成方法はご覧のとおりスクリプトブロックのGetNewClosure()メソッドの呼び出しです。
  • クロージャのブロック内で「$script:i++」とスコープを指定しているのは親ブロック側の変数を変更してやるためです。ただし、本当は親ブロックの変数なのでスクリプトスコープにする必要はないのですが、Set-Variableでやるのも面倒でこうしました。実行結果の通りスクリプト内で$iに値を設定しても影響を受けないので、クロージャ内の環境に保持されているはずです。
  • 「$a=Accum 100」で変数$aにクロージャを代入(変数を束縛?)して、その後そのクロージャを呼び出していますが、PowerShellの場合にその呼び出し方は2種類あるようです。
    1. 「&$a」 ヘルプのabout_functionsにはこちらが書かれていて、ネット上もそのようにやっている例が散見されました。一般的にはこちらを使用することが多いようです。(追記:&はローカルコンピュータ上のInvoke-Commandと同義で、スクリプト ブロックの文字列をコマンドとして評価または実行する)
    2. 「. $a」 いわゆるソース呼び出しなのですが、こちらも確かにクロージャとして動作しています。また、こちらの呼び出し方の場合にはクロージャのスコープが親スコープと同一となるようで上述したscriptスコープの指定がなくても動くみたいです。ソース呼び出しの場合にはスコープが変わるというのは言われてみればそうかもしれません。でも、クロージャのスコープ自体は不思議なものですね。


とりあえずクロージャが動くようなので、どこかでもう少し別のサンプルを作りたいと考えています。

関連記事

クロージャあり〼その2

加筆


  • クロージャという言葉を調べてみると、無名関数のことをクロージャと呼んだりしている場合もあり、上記の説明では「環境を含んだ関数」という意味で記述しているので厳密な意味についてはご勘弁を。
  • マーチン・ファウラーは無名関数にせよ環境込みの関数にせよ「意識しなくてもいいぐらいに手軽にクロージャを使えること」が大事だと書いていた。そういう意味ではPowerShellのクロージャは合格点がもらえるレベルかどうか疑問です。今のところ。
  • 0 件のコメント:

    コメントを投稿