Skip to content

Instantly share code, notes, and snippets.

@guitarrapc
Last active December 13, 2017 19:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guitarrapc/e93da01cb138a1ecf042d03e50be2e0b to your computer and use it in GitHub Desktop.
Save guitarrapc/e93da01cb138a1ecf042d03e50be2e0b to your computer and use it in GitHub Desktop.
PowerShell Closure scenarios
class foo {
$n = 1
$o = 1
$a = 1
[int] f() {
$this.n = $this.n + 1
return $this.n
}
[int] g() {
$f = {
$this.o = $this.o + 1
}
& $f
return $this.o
}
[int] h() {
$f = {
# ここでインスタンス変数へのアクセスは禁止となる
$this.a = $this.a + 1
}.GetNewClosure()
& $f
return $this.a
}
[int] i() {
$p = 1
$f = {
$p = $p + 1
}.GetNewClosure()
& $f
return $p
}
[int] j() {
$q = 1
return & {
$q = $script:q + 1
$q
}.GetNewClosure()
}
[scriptblock] k() {
$r = 1
return {
$r = $script:r + 1
$r
# 常時 1を示す
# Get-Variable -Name r -Scope script
# 常時 2をしめる
# Get-Variable -Name r -Scope local
}.GetNewClosure()
}
}
$f = [foo]::new()
$n = 0 # (2)
# クラスはレキシカルスコープなので期待通りになる
$f.f() # actual : 2 (expect : 2)
$f.f() # actual : 3 (expect : 3)
$f.f() # actual : 4 (expect : 4)
# クラス内部のスクリプトブロックからインスタンス変数を変更は、無名関数による実行として期待通りになる
$f.g() # actual : 2 (expect : 2)
$f.g() # actual : 3 (expect : 3)
$f.g() # actual : 4 (expect : 4)
# this アクセスはクロージャ生成時点でできなくなるためエラーとなる
# The property 'p' cannot be found on this object. Verify that the property exists and can be set.
$f.h()
# ローカル変数の操作を行っても結果は0のまま
$f.i() # actual : 1 (expect : 2)
$f.j() # actual : 2 (expect : 2)
$f.j() # actual : 2 (expect : 3)
$f.j() # actual : 2 (expect : 4)
& $f.k() # actual : 2 (expect : 2)
& $f.k() # actual : 2 (expect : 3)
& $f.k() # actual : 2 (expect : 4)
function foo {
$m = 1 # 取得されない
$f = {
$m = $m + 1
$m
}
$f
}
function goo {
$n = 1 # 取得される
$f = {
$n = $n + 1
$n
}.GetNewClosure()
$n = 0
$f
}
function hoo {
$o = 1 # 取得される
$f = {
$script:o = $o + 1
$o
# Get-Variable o -Scope local 存在しない
# Get-Variable o -Scope script この script スコープはクロージャ内部のスクリプトスコープであって、外部のスクリプトスコープとは違う
# 結果、2,3,4 はここで確認できる
}.GetNewClosure()
$f
}
function ioo {
$p = 1
$f1 = {
$script:p = $p + 1
$p
}.GetNewClosure()
$f2 = {
$script:p = $p + 1
$p
}.GetNewClosure()
$f1, $f2
}
function joo {
$q = 1
$f1 = {
$script:q = $q + 1
$q
}.GetNewClosure()
$f2 = {
$script:q = & $f1 + 1
$q
}.GetNewClosure()
$f1, $f2
}
$f = foo
$g = goo
$h = hoo
$i1, $i2 = ioo
$j1, $j2 = joo
$m = 0
$n = 0
$o = 0
$p = 0
$q = 0
# GetNewClosure() を使っていないと外部変数の状態はスクリプトブロック内に束縛されない + スクリプトブロックの中身は常に新規実行なので1
& $f # actual : 1 (expect : 2)
& $f # actual : 1 (expect : 3)
& $f # actual : 1 (expect : 4)
# GetNewClosure() でクロージャと化すことでその前までに宣言されたクロージャ外の変数を内部で束縛する。
# しかし、内部でscript: スコープで指定しないと、ローカルスコープ変数 (この例では宣言なし = 0相当)を作るため常時2となる
# 束縛した変数は.GetNewClosure() 実行時点でのコピーなので、クロージャ後の変数の変更は影響うけない
& $g # actual : 2 (expect : 2)
& $g # actual : 2 (expect : 3)
& $g # actual : 2 (expect : 4)
# GetNewClosure() でクロージャと化すことでその前までに宣言されたクロージャ外の変数を内部で束縛する。
# 内部でscript: スコープで指定したのでクロージャrの内部スコープ変数となり再実行でも束縛された値に引き続きアクセスできる
# 束縛した変数は.GetNewClosure() 実行時点でのコピーなので、クロージャ後の変数の変更は影響うけない
& $h # actual : 2 (expect : 2)
& $h # actual : 3 (expect : 3)
& $h # actual : 4 (expect : 4)
# クロージャの中の script: スコープは、クロージャ外部の script: スコープとは分離されている
Get-Variable o -Scope script # actual : 0 (expect? : 4)
# f1 内部の変数はscriptスコープのようでクロージャ内部までしか影響しないため f2 からはアクセスできない
& $i1 # actual : 2 (expect : 2)
& $i2 # actual : 2 (expect : 3)
& $i1 # actual : 3 (expect : 4)
& $i2 # actual : 3 (expect : 5)
# f1 の計算結果を f2 で利用すればいいがどうなのこれ
& $j1 # actual : 2 (expect : 2)
& $j2 # actual : 2 (expect : 3)
& $j1 # actual : 3 (expect : 4)
& $j2 # actual : 3 (expect : 5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment