PowerShellの罠:クロージャを使う必要がある第一級関数呼び出しの罠

別にpowershellに起因する問題ではないが、ループでイベントハンドラを設定して各ボタンで少しずつ異なる動作をさせたい時によくある、実行時にループ後の変数値が参照されてしまうバグはpowershellでも発生する。
解決方法はクロージャを使うこと。
ただしPowerShellではクロージャを使いたいとき、明示的に「GetNewClosure」を呼び出さないといけない。
また普通の言語とはすこしクロージャの挙動が違うので書き方に注意が必要である。

# (1) 以下を実行すると、ループ後の$iを使って処理が実行されるため期待に反して6が5つ出力がされる
# 6
# 6
# 6
# 6
# 6
$funcList = @()
for ($i = 1; $i -le 5; $i++) {
    $funcList += { Write-Output $i }
}
$funcList | % { & $_ } # 各関数を実行

# (2) 以下を実行すれば、ループ時の$iを使って処理が実行されるため期待通り1~5が出力される
# 1
# 2
# 3
# 4
# 5
$funcList = @()
for ($i = 1; $i -le 5; $i++) {
	$funcList += { Write-Output $i }.GetNewClosure()
}
$funcList | % { & $_ } # 各関数を実行

# (3) 普通の言語のクロージャは「親スコープの参照」を保持するので(2)でも問題は解決せず、以下のようなコードでないと期待通り動かない筈である。
# しかしPowerShellのGetNewClosureは、「親スコープが持つ変数のコピー」を保持するので(2)のコードで期待通り動作する。
# まあPowerShellで(3)を書いても期待通りに動作はするが無駄に複雑になる。
$funcList = @()
for ($i = 1; $i -le 5; $i++) {
    $f = &({ # 即時実行スクリプトブロックで、新たなスコープを作る
        $newScopeVariable = $i
        return { Write-Output $newScopeVariable }.GetNewClosure()
    })
    $funcList += $f
}
$funcList | % { & $_ } # 各関数を実行