2011年10月30日日曜日

Groovy Curry化の勉強のはずがっ Scope の調査に…

スコープってどうなってるの?



どうやら、この前書いたエントリで不思議に思ったことがあったので、
ちょいと調べて見ました。

それはクロージャーを返すクロージャーで宣言されているあの数値、
スコープはどうなっているのかということです。

試してみましょう。

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



はい、通りました。
予測は正しかったようです。


結論

クロージャーを返すクロージャーを書く際には、戻り値となるクロージャーの中に使われる変数は極力元のクロージャーにて定義した変数を使うようにすること。



0 件のコメント:

コメントを投稿