17250


クロージャーの理解に苦慮しています。  
以下にサンプルプログラムを添付します。swiftプログラムです。  
動作については理解していますが、関数(func)内の関数で、内部の関数はその外の変数にアクセス可能というクロージャーの理解はしています。疑問点は以下の通りです。

[code]

    let inc = makeIncrementer(10, 5)
    
[/code]

によって、incの定数に"15"が設定されますよね。

[code]

    inc()   // 15
    
[/code]

で、inc()とは何？という疑問があります。またinc()を繰り返す度に5が足されるのか、makeIncrementerのaddValueの値が5を足されると思いますが、var
vがなぜ値を保持しているのかも疑問です。

何か理解が間違っていると思いますので、教えていただけないでしょうか？質問を端的に言うとinc()とは何？ということです。

以下、リストを添付します。よろしくお願いします。

[code]

    func makeIncrementer(initValue: Int, addValue: Int) -> () -> Int {
        var v = initValue
        func incrementer() -> Int {
            v += addValue
            return v
        }
    
        return incrementer
    }
    
    let inc = makeIncrementer(10, 5)
    inc()   // 15
    inc()   // 20
    inc()   // 25
    
[/code]


CC BY-SA 3.0
2015-10-03T08:08:29.900
17214
2015-10-05T14:16:38.130
2015-10-05T12:54:05.720
8000
8593
question
swift クロージャ
func内の変数保持について(クロージャーの理解)


[詳解Swift 荻原剛志著](http://www.sbcr.jp/products/4797380491.html)

この書籍の、「Chapter12 クロージャ、12.2
変数のキャプチャ」に、説明があります。ひとことで説明すると、クロージャがローカル変数を参照し続けているので、ローカル変数は解放されず、メモリに保持されたままになる、ということだそうです。誤解を与えてはいけないので、詳しくは、この書籍に直接あたってください。

クロージャ（というより、この場合はネスト関数）を解放すれば、ローカル変数も解放されます。

[code]

    // 解放されるタイミングを調べるため、deinitを実装したクラスを定義する。
    class MyClass {
        var lValue: Int = 0
    
        init(lValue: Int) {
            self.lValue = lValue
        }
    
        deinit {
            print("Released with \(lValue)") // 解放された時点のlValueの値を出力
        }
    }
    
    func makeIncrementer(lValue a: MyClass, rValue b: Int) -> () -> Int {
        var innerValue = a.lValue
        func incrementer() -> Int {
            innerValue += b
            a.lValue = innerValue
            return innerValue
        }
        return incrementer
    }
    
    // nilを代入して解放するためにOptional型で宣言する。
    var myClass: MyClass! = MyClass(lValue: 20)
    // nilを代入して解放するためにOptional型で宣言する。
    var innerFunc: (() -> Int)! = makeIncrementer(lValue: myClass, rValue: 10) // 20に10を加算する。
    innerFunc() // 30
    innerFunc() // 40
    myClass = nil // myClassを解放しようとするが、deinitが呼ばれない。
    innerFunc() // 50 
    innerFunc = nil // 出力：Released with 50
    // innerFuncを解放した時点で、myClassが解放されたことがわかる。
    
[/code]


CC BY-SA 3.0
2015-10-03T09:45:51.283
17216
2015-10-04T04:07:39.953
2015-10-04T04:07:39.953
7362
7362
17214
answer


>
[code]

>     let inc = makeIncrementer(10, 5)
>  
[/code]

>
> によって、incの定数に"15"が設定されますよね。

ちがいます。これにより`inc`が保持するのは関数オブジェクトです。`makeIncrementer`の定義を見れば分かるようにreturnしているのはIntではなく`incrementer`という関数です。この時点では、関数オブジェクトが作られただけで、まだ実行されていません。

このようにして作られた関数オブジェクトはその後の

[code]

    inc()
    
[/code]

で始めて実際に実行されます。

`incrementer`の中で参照されている`v`や`addValue`のスコープは、`makeIncrementer`ローカルです。本来は`incrementer`を抜けた時点で消滅するのですが、クロージャである`incrementer`が`v`や`addValue`を参照し続けているため、スコープを抜けても残ったままになります。そのため、`addValue`は`makeIncrementer`を呼び出したときの`5`がいつまでも残っていますし、`v`は実行する度に5ずつ増えていきます。

なお、もう一度`makeIncrementer`を呼び出したときにまた作られる`v`や`addValue`や関数オブジェクトは先ほど作成したものとは別になります。

[code]

    let inc = makeIncrementer(10, 5)
    inc()   // 15
    let inc2 = makeIncrementer(3, 1)
    inc2()  // 4
    inc()   // 20
    inc2()  //5
    
[/code]


CC BY-SA 3.0
2015-10-03T23:14:30.770
17235
2015-10-04T10:09:53.777
2015-10-04T10:09:53.777
5793
5793
17214
answer


> var vがなぜ値を保持しているのかも疑問です。

Swift の開発環境が手元にないので、golang で同じ様なモノを書いてみました。

**counter.go**

[code]

    package main
    
    import "fmt"
    
    func newCounter(initValue, addValue int) (func() int, func() int, func()) {
        v := initValue
    
        inc := func() int {
            v += addValue
            return v
        }
    
        get := func() int {
            return v
        }
    
        reset := func() {
            v = initValue
        }
    
        return inc, get, reset
    }
    
    func main() {
        inc, get, reset := newCounter(10, 5)
    
        fmt.Println(inc())  // => 15
        fmt.Println(inc())  // => 20
        reset()
        fmt.Println(get())  // => 10
    }
    
[/code]

もっと本来的な書き方があるはずですが、それはさておいて、`newCounter` 関数内にある変数 `v` がどの様に扱われているのかを見てみます。

[code]

    $ go run -gcflags='-m=1' counter.go
               :
    ./counter.go:6: moved to heap: v     // v := initValue
    ./counter.go:9: &v escapes to heap   // v += addValue (in inc())
    ./counter.go:14: &v escapes to heap  // return v (in get())
    ./counter.go:18: &v escapes to heap  // v = initValue (in reset())
    
[/code]

スタック領域ではなくヒープ領域に移動させられていて、そのメモリ領域を `inc()`, `get()`, `reset()`
関数では参照しています。おそらく、swift でも同じ事をしているのではないかと推測します。

# 処理系の中には必要に応じて被参照変数をスタック <-> ヒープ間でコピーしていたりします


CC BY-SA 3.0
2015-10-04T10:20:30.763
17245
2015-10-04T10:20:30.763
17214
answer


> Swift closures and Objective-C blocks are compatible, so you can pass Swift
> closures to Objective-C methods that expect blocks. Swift closures and
> functions have the same type, so you can even pass the name of a Swift
> function.
>
> Closures have similar capture semantics as blocks but differ in one key way:
> Variables are mutable rather than copied. In other words, the behavior of
> __block in Objective-C is the default behavior for variables in Swift.
>
> [Interacting with Objective-C APIs - Using Swift with Cocoa and Objective-C
> (Swift
> 2)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-
> CAPIs.html)

にある通り、Swiftのクロージャは、Objective-Cのblocksと互換性を持つものです。

そちらの実装については公開されている情報が多いため、blocksについて知ることでクロージャについて理解が深まると思います。

両者の動作のの違いとして、Objective-
Cにおけるblocksは、自動変数のキャプチャを値のコピーで行っていましたが、Swiftのクロージャでは常に`__block`が指定されているものとして扱います。

さて、blocksがどう実装されているかというと、この実体はObjective-
Cクラスのインスタンスです（この説明は厳密には正しくないのですが、Swiftにおいてクロージャが参照型に分類されていることから、クラスだと考えた方が楽だと思います）。

変数のキャプチャは、このクラスのインスタンス変数になることを意味します。Swiftでは必ず`__block`指定子でキャプチャされることは既に述べました。このとき、`__block`ストレージ型として扱われます。

> __block変数は、その変数のレキシカルスコープと、その変数のレキシカルスコープ内で宣言または  
>  作成されたすべてのブロックおよびブロックのコピーとの間で共有されるストレージ内に存在しま  
>  す。したがって、このストレージは、スタックフレーム内で宣言されているブロックのコピーが、フ  
>  レームの終了後も存続する場合は（たとえば、後で実行するために、どこかのキューに入れられてい  
>  る場合）、そのスタックフレームが破棄された後も存続します。特定のレキシカルスコープ内の複数  
>  のブロックが、同時に1つの共有変数を使用できます。
>
> 最適化のために、ブロックストレージは、ブロック自身と同様に、スタック上に置かれます。  
>  Block_copyを使用してブロックがコピーされた場合（または、Objective-Cで、そのブロックにcopy  
>  が送信された場合）は、変数はヒープにコピーされます。したがって、__block変数のアドレスは時  
>  間の経過とともに変化する可能性があります。
>
> [ブロックプログラミングトピック](https://developer.apple.com/jp/documentation/Blocks.pdf)

要約すると、ローカル変数や関数の引数など、現在の環境（レキシカルスコープ）を、ランタイムが上手く管理することで、共有できるようにしてくれる、ということです。

Objective-
Cにおいては、blocksをスタックもしくはヒープのどちらで管理するべきかコンパイラが判断できなかった場合に、必要に応じて明示的に`copy`を呼ぶ辛い場面もありましたが、Swiftのクロージャでは意識する必要はありません。

`__block`ストレージ型の`var
v`をインスタンス変数に持つ、クロージャ`incrementer`が、`inc`の正体である…、と考えるのが比較的分かりやすいと思います。


CC BY-SA 3.0
2015-10-04T12:17:06.623
17246
2015-10-04T12:17:06.623
5337
17214
answer


`inc()`は、`let inc`の定数名に、incrementer関数へ渡す引き数`()`をつけたものです。inc
は、incrementerの略だと思われます。

関数は、引き数を渡した時点で `return`にあるものを返しますから、makeIncrementer関数内の

[code]

    return incrementer
    
[/code]

では、まだ incrementer関数の引き数は渡していないため、incrementer関数は実行されていません。これは、incrementer関数への
**参照** を`return`で返している状態です。

[code]

    let inc = makeIncrementer(10, 5)
    
[/code]

では、makeIncrementer関数のパラメータ`(initValue: Int, addValue: Int)`に適合する引き数`(10,
5)`を渡していますので、makeIncrementer関数が実行されています。`() -> Int`という関数型の関数
incrementerへの参照が`return`で返され、定数 incに割り当てられています。定数名を Option＋クリックすると、定数
incが関数型となっている様子が確認できます。（Playgroundでは右側でも見れます。）

[![画像の説明をここに入力](https://i.stack.imgur.com/gVdui.png)](https://i.stack.imgur.com/gVdui.png)

ですが、incrementer関数のパラメータ`()`に適合する引き数`()`は渡されていないため、incrementer関数はまだ実行されていません。Int型の`15`という値は返ってきていません。定数
incは、incrementer関数を参照している状態です。

`let inc` → `func incrementer`を参照  
`func incrementer` → `func makeIncrementer` の`var v`と`addValue`を参照

と、`let inc`から makeIncrementer関数のローカル変数／定数まで、参照がつながっています。`let inc`が
incrementer関数を参照している限り、`var v`と`addValue`も参照され続けるということです。

参照されている間は状態が維持されますから、makeIncrementer関数の実行が終わっても、v変数と
addValue定数は存在し続けます。inc定数からの参照の連鎖が、v変数と addValueを繋ぎとめているからです。仮に、

[code]

    let inc = makeIncrementer(10, 5)()
    
[/code]

とここで、incrementer関数の引き数`()`を渡すと、`Int`が`return`で返されて、関数は実行終了となります。定数
incに、Int型の`15`が入った状態です。この定数 incと incrementer関数は参照関係にはなりません。

[![画像の説明をここに入力](https://i.stack.imgur.com/6O0U5.png)](https://i.stack.imgur.com/6O0U5.png)

参照関係のときに何が起きているかを確かめるために、以下のように、コードの各所に`print`を追加してみてはいかがでしょうか。 （Xcode 7、Swift
2で書いています。）

[code]

    func makeIncrementer(initValue: Int, addValue: Int) -> () -> Int {
        print("makeIncrementer関数の実行")
        print("initValue: \(initValue), addValue: \(addValue)")
    
        var v = initValue
        print("makeIncrementer内 var v = \(v)")
    
        func incrementer() -> Int {
            print("incrementer関数の実行")
            print("参照している v = \(v), addValue = \(addValue)")
    
            v += addValue
            print("incrementer return v = \(v)")
            return v
        }
        return incrementer
    }
    
    print("----- let inc 宣言 -----")
    let inc = makeIncrementer(10, addValue: 5)
    
    print("----- inc() １回目 -----")
    inc()
    
    print("----- inc() ２回目 -----")
    inc()
    
    print("----- inc() ３回目 -----")
    inc()
    
[/code]

コンソールを見ると、`inc()`が、makeIncrementer関数は実行しないで、incrementer関数だけを実行している様子が確認できます。inc定数は、makeIncrementer関数ではなく、incrementer関数を直に参照しているからです。

[![画像の説明をここに入力](https://i.stack.imgur.com/DLFcz.png)](https://i.stack.imgur.com/DLFcz.png)

`inc()`を呼び出したときに、新たに`initValue`に`10`が代入されて……というような
makeIncrementer関数の処理は行われません。よって、v変数に新しい
initValue値が代入されることもありません。このincrementer関数が参照している`v`は、関数実行終了後も
inc定数から続く参照によってそのまま保持され、また新たに別の場所で`inc()`と書いてincrementer関数を呼び出しても、inc定数から続く参照の連鎖につながっているままの値を使うことになる、という訳です。

定数や変数は、値や参照を保持するのが仕事ですから、定数や変数から参照の連鎖がつながっているローカル変数／定数は結果、保持されることになります。関数は処理を実行するのが仕事ですから、関数のクロージャ内から外の変数／定数への参照は、実行時のみの一時的なものとなります。が、定数や変数がその関数を参照している場合には、定数／変数が参照の連鎖をどこまでもたどって、参照状態とその先にある値を保持してくれるようです。（厳密にシステムがどう働くかという視点で見ると、この説明はちょっと違うと思いますが、今ひとまずのところはこのようなイメージが理解の助けになるのではないでしょうか。詳しいことは、他の方のご回答をご覧いただければと思います。）


CC BY-SA 3.0
2015-10-04T20:35:16.473
17250
2015-10-05T14:16:38.130
2015-10-05T14:16:38.130
9833
9833
17214
answer


他の方の回答で「何が起きているか」についての説明は十分と思いますが、関連するキーワードが出ていないので補足します。

プログラミング言語の一般的概念として、変数には「スコープ」と「エクステント」という属性があります。「スコープ」はその変数がコードのどの場所から見えるのか、「エクステント」はその変数がいつからいつまで存在するのか、です。

例えばCでは以下の変数がサポートされています：

  * グローバル変数: スコープはプログラム全体。エクステントはプログラム起動から終了まで
  * トップレベルのstatic変数: スコープはファイル内。エクステントはプログラム起動から終了まで
  * 関数内static変数: スコープは関数内。エクステントはプログラム起動から終了まで
  * auto変数: スコープはブロック内。エクステントは変数の定義からブロックを抜けるまで

このうち、最後のauto変数を「ローカル変数」と呼ぶことが多いです。このことから、Cおよびその派生言語では「ローカル変数」というのに「スコープもエクステントもブロック内のみ」というイメージがついてしまっています。

しかし、他の言語、特に関数オブジェクト(第一級関数)を持つ言語では「ローカル変数」は

  * スコープはブロック内。エクステントはそのブロックに入った後からずっと

とすることが多いです。ずっと、というのは意味的にはプログラム終了までずっと存在し続けるということです。現実には、どこからも参照されなくなった時点でGCが回収してくれるのでメモリの心配をする必要はありませんが。

今回の例で言えば、var
vのスコープはmakeIncrementerの中、つまりその中に書かれたコードからしか読み書きできないが、makeIncrementerに入るたびに新たなvが作られて、それは(GCで回収されるまで)ずっとメモリ上に残っている(無限エクステント)、ということになります。

Cのauto変数のようにスコープとエクステントをブロックに合わせる方式では、変数はスタックで管理できますが、「ブロックスコープ、無限エクステント」のローカル変数では管理は多少複雑になります。が、そのへんは言語処理系がうまい具合に取り計らってくれるので、性能チューニングをしているのでもなければあまり気にする必要はありません。


CC BY-SA 3.0
2015-10-04T21:45:53.943
17251
2015-10-04T21:45:53.943
5258
17214
answer