PowerShell覚え書き
お題
前回のブログでPowerShellのクロージャ機能を使ってみました。せっかくなので、バリエーションとして一つの環境(クロージャ)を複数の関数から共有するようなサンプルを作りました。(Paul GrahamのANSI Common Lispからのパクリです。)前回と同じくアキュムレータなのですが、リセット機能ありのバージョンです。function Accum() { $i=0 $closure={ $obj = New-Object PSObject $fn = { $script:i = 0 } Add-Member -inputobject $obj -membertype scriptmethod -name reset -value $fn $fn = {$script:i++ ;$i } Add-Member -inputobject $obj -membertype scriptmethod -name stamp -value $fn return $obj }.GetNewClosure() return &$closure } $a=Accum #疑似コンストラクタ "stamp => "+$a.stamp() "stamp => "+$a.stamp() "stamp => "+$a.stamp() "reset "+$a.reset() "stamp => "+$a.stamp() "stamp => "+$a.stamp()
実行結果は以下の通り。
-
stamp => 1
stamp => 2
stamp => 3
reset
stamp => 1
stamp => 2
メモ
- 着想はGetNewClosure()はブロックの持つメソッドなので、ブロック内でオブジェクトを作って複数のメソッドを用意すれば環境を複数のインターフェース間で共有できるということです。
- 関数Accum内でクロージャ定義だけでなく、&でのクロージャ呼び出しを返しているので、利用者側には一種のオブジェクトのように見え、メソッドの呼び出しもスムーズです。
- クロージャを使うことでカプセル化ができたので、オブジェクト指向的な使い方ができますね。(誰かのブログでclass定義用のライブラリを作っている人がいましたが、クロージャを使ってたのかな。。)
- ブロック内の変数のスコープの書き方が面倒な点は相変わらずです。なにかよい方法はないのか。
クロージャで面白いことがまだできそうです。どこかでもう少し他のサンプルを作りたいと考えています。
追記(2012/6/2)
遊び半分で上記ソースをオブジェクト指向っぽく?改変してみました。function new([String]$cname) { $obj = New-Object PSObject $sb=Get-Variable $cname -valueonly & ($sb.GetNewClosure()) #Closureを作成し同一ブロック内でブロック呼出し return $obj } function method([String]$mname,[ScriptBlock]$msb) { Add-Member -inputobject $obj -membertype scriptmethod ` -name $mname -value $msb -Force } function PSClass([String]$cname,[ScriptBlock]$csb) { Set-Variable -name $cname -value $csb -scope script } <#------------------------------------------------------------#> PSClass Accum { $i=0 method reset { $script:i = 0 } #スコープ修飾はまだ必要 method stamp { $script:i++ ;$i } #スコープ修飾はまだ必要 } <#------------------------------------------------------------#> $i=50 $a=new Accum #疑似コンストラクタ $b=new Accum #疑似コンストラクタ "`$a.stamp() => "+$a.stamp() # $a.stamp() => 1 "`$a.stamp() => "+$a.stamp() # $a.stamp() => 2 "`$b.stamp() => "+$b.stamp() # $b.stamp() => 1 "`$b.stamp() => "+$b.stamp() # $b.stamp() => 2 "`$a.stamp() => "+$a.stamp() # $a.stamp() => 3 "`$a.stamp() => "+$a.stamp() # $a.stamp() => 4 "`$a.reset() "+$a.reset() # $a.reset() "`$a.stamp() => "+$a.stamp() # $a.stamp() => 1PSObject作成や、メソッドの追加などは関数化できたのですが、肝心のインスタンス変数をメソッドから参照するにはscope指定が必要となってしまいました。インスタンス変数定義用のメソッドを用意したり、文字列マッチングしてメソッド内の変数にスコープを自動追加することもできたのですが、本筋から外れていくのでこれ以上はやめました。
結果的には、糖衣構文ならぬ
0 件のコメント:
コメントを投稿