スコープってどうなってるの?
どうやら、この前書いたエントリで不思議に思ったことがあったので、
ちょいと調べて見ました。
それはクロージャーを返すクロージャーで宣言されているあの数値、
スコープはどうなっているのかということです。
試してみましょう。
scope.groovy
def scopedCounter = {
def c = 0
return {c+=1}
}
def sCnt = scopedCounter()
def tCnt = scopedCounter()
(1..10).each {
assert sCnt() == it
assert tCnt() == it
}
特に問題は発生しませんでした。
したがって、
scopedCounter
にて定義されている変数
c
のスコープはそれぞれのクロージャーに閉じているようです。
そういえばっdef
外せるのかな?
こんなことを考えなければ、よかった。
しかし、考えてしまったので仕方がない、調べてみました。
unscope.groovy
def unscopedCounter = {
cnt = 0
return {cnt+=1}
}
def uCnt = unscopedCounter()
def vCnt = unscopedCounter()
def escape = 0
(1..10).eachWithIndex { value, indx ->
escape = indx
assert uCnt() == value + escape
assert vCnt() == value + escape + 1
}
assert cnt == 20
失敗すること3回くらい。
やっとなんとかつかめてきました。
クロージャーの中に使われている変数の宣言で
def
を省略すると、そのスコープは一つ大きなグローバルに含まれるようになる。
書き方を正すなら、先のコードは次のコードと同じ意味になる。
unscope.groovy
def cnt = 0
def unscopedCounter = {
cnt = 0
return {cnt+=1}
}
def uCnt = unscopedCounter()
def vCnt = unscopedCounter()
def escape = 0
(1..10).eachWithIndex { value, indx ->
escape = indx
assert uCnt() == value + escape
assert vCnt() == value + escape + 1
}
assert cnt == 20
まず、グローバルスコープで
cnt
を宣言しておいて、その後、クロージャー
unscopedCounter
が呼ばれるたびに、0
が設定される。では、そのあたり本当か試してみましょう。
unscope.groovy
def unscopedCounter = {
cnt = 0
return {cnt+=1}
}
def uCnt = unscopedCounter()
def vCnt = unscopedCounter()
def escape = 0
(1..10).eachWithIndex { value, indx ->
escape = indx
assert uCnt() == value + escape
assert vCnt() == value + escape + 1
}
assert cnt == 20
def wCnt = unscopedCounter()
assert cnt == 0
(1..10).each {
assert wCnt() == it
}
assert cnt == 10
はい、通りました。
予測は正しかったようです。