2019/12/13 Swift クロージャー パート3(capturing)

Capturing Values(キャプチャ)

sample1-1

func createCounter(countBy countBy:Int)->()->Int{
    var count:Int=0
    return {()-> Int in
        count += countBy
        return count
    }
}

var counter1=createCounter(countBy: 8)
print(counter1())  //8
print(counter1())  //16
print(counter1())  //24

var counter2=createCounter(countBy: 10)
print(counter2())  //10
print(counter2())  //20
print(counter2())  //30
print(counter2())  //40

var counter3=counter2
print(counter3())  //50
print(counter3())  //60
print(counter3())  //70

counter2=createCounter(countBy:3)
print(counter2())  //3
print(counter2())  //6
print(counter2())  //9
print(counter3())  //80
print(counter3())  //90
print(counter3())  //100

 クロージャー(createCounter関数内の赤文字部分↓)は自分自身が定義されているコンテキスト(createCounter内)から定数・変数(変数countと引数countBy)をキャプチャする。定数・変数が定義されているスコープがすでに存在しないとしても(外側の関数の実行が終わっても)、クロージャーは定数・変数を参照・修正できる。

func createCounter(countBy countBy:Int)->()->Int{

    var count:Int=0

    return {()-> Int in

        count += countBy

        return count

    }

}

 Swiftでは値をキャプチャできる最もシンプルな形のクロージャーは他の関数の本体内に定義されたネスト(入れ子)された関数である。ネスト(入れ子)された関数は外側の関数(createCounter)の引数(countBy)と、外側の関数の内側で宣言された定数・変数(count)をキャプチャできる。

func createCounter(countBy countBy:Int)->()->Int{
    var count:Int=0
    return {()-> Int in
        count += countBy
        return count
    }
}

var counter1=createCounter(countBy: 8)
print(counter1())  //8
print(counter1())  //16
print(counter1())  //24

var counter2=createCounter(countBy: 10)
print(counter2())  //10
print(counter2())  //20
print(counter2())  //30
print(counter2())  //40

var counter3=counter2
print(counter3())  //50
print(counter3())  //60
print(counter3())  //70

counter2=createCounter(countBy:3)
print(counter2())  //3
print(counter2())  //6
print(counter2())  //9
print(counter3())  //80
print(counter3())  //90
print(counter3())  //100

sample1-1でcreateCounter内のクロージャーが変数countと引数countByを、自身の周りのコンテキストからキャプチャします。countとcountByの値をキャプチャした後クロージャーはcreateCounterにより返り値として返されます。

createCounterにより返されるクロージャーは、呼び出される度にcountの値をcountByだけ増分させるクロージャーです。

createCounterが返す返り値の型は ()->Int です。つまりただの値ではなくクロージャーを返すということです。引数無し、Int型の値を返すクロージャーです。

createCounter関数はcountという名のInt型の変数を定義しています。createCounterが返すクロージャーが呼び出される度に加算される合計値を格納する変数です。変数countは0で初期化されます。

createCounter関数はargument labelがcountBy、parameter nameもcountByであるInt型の引数を一つ取ります。この引数は、createCounterが返すクロージャーが呼び出される度にcountをどれだけ増分させるかを示すInt型の数値です。

createCounter内で定義されるクロージャーが、実際にcountを増分させます。このクロージャーはシンプルにcountをcountByの値だけ増分させます。そして増分後の値を返します。

{()-> Int in

        count += countBy

        return count

    }

 クロージャーは引数無しで、自身の内部でcountとcountByを参照します。これは、クロージャーの周囲からcountとcountByへの参照をキャプチャして、それをクロージャー内で使うことで実現しています。参照をキャプチャすることで、createCounter関数の実行が終了した後もcountとcountByは消えず、クロージャーが次に呼び出しされる時にcountが利用できることが保証されます。

sample1-1(再掲)

func createCounter(countBy countBy:Int)->()->Int{
    var count:Int=0
    return {()-> Int in
        count += countBy
        return count
    }
}

var counter1=createCounter(countBy: 8)
print(counter1())  //8
print(counter1())  //16
print(counter1())  //24

var counter2=createCounter(countBy: 10)
print(counter2())  //10
print(counter2())  //20
print(counter2())  //30
print(counter2())  //40

var counter3=counter2
print(counter3())  //50
print(counter3())  //60
print(counter3())  //70

counter2=createCounter(countBy:3)
print(counter2())  //3
print(counter2())  //6
print(counter2())  //9
print(counter3())  //80
print(counter3())  //90
print(counter3())  //100

9行目で、変数counter1に「呼び出される度に変数countに8ずつ加算するクロージャー」への参照をセットしています。counter1にはクロージャーを代入しているのと同じ状態なので、counter1を実行することができます。(10,11,12行目)

14行目で二つ目のカウンターを作っています。変数counter2には、9行目でcounter1にセットされたクロージャーとは別の新しいクロージャーへの参照がセットされ、そのクロージャーは9行目で作られたcountとは別の新しいcountを増分させます。

counter1を実行する度にcounter1に対応したcountが増分されますが、それはcounter2にセットされたクロージャーがキャプチャしているcountには影響を与えません。

Closures Are Reference Types

「ある変数あるいは定数」に「関数あるいはクロージャー」を代入するということは、実際にはその「ある変数あるいは定数」を「関数あるいはクロージャー」の参照として設定することです。sample1-1で言えば、counter1が参照するクロージャーを選んだ、ということでcounter1にクロージャー本体が代入された訳ではありません。

それは、二つの異なる定数・あるいは二つの異なる変数に、クロージャーを代入すると、両方の定数・あるいは変数が同一のクロージャーを参照することを意味します(20行目以降)。

 

 

参考:

https://docs.swift.org/swift-book/LanguageGuide/Closures.html

コメントを残す

メールアドレスが公開されることはありません。