質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

Q&A

解決済

1回答

509閲覧

[swift] クロージャの呼ばれ方について

jantyran

総合スコア14

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

0グッド

0クリップ

投稿2018/03/09 05:43

クロージャの呼び出し方が気になったので質問させてください。

1.そもそもの話しなのですが、クロージャは下記のコードの様に
"func 何処かで作られたクロージャ名 (){}"
で呼び出せるのか。

2.下記コードの様に、ローカルスコープ(useClosure())内に入っていようとも外からクロージャを呼び出せるのか。

3.複数の同名のクロージャが存在している場合(複数のローカルスコープ内にそれぞれ)どのクロージャが呼び出されるのか。

基礎的な部分で申し訳ないのですが、調べてもよくわからなかったので教えていただけますと幸です。
よろしくお願いします。

以下コード

class JunkClass3 var name = "" /// クロージャーを使用する例 func useClosure() { let no = 20 // 接尾形式でクロージャーを渡す execClosure() { // 自動でキャプチャーされるのでそのまま使用可能 print("no: (no)") // この場合、この書き方ではエラーになる // → 周囲の環境にnameが見つからないため。かな? // print("name: (name)") // クロージャーの中でプロパティを参照するためにselfを使用 // → selfがキャプチャーされ、self.nameが使用可能になる print("name: (self.name)") } } /// 渡されたクロージャーを呼び出すだけのメソッド func execClosure(closure: (()->Void)) { closure() } }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

クロージャは下記のコードの様に"func 何処かで作られたクロージャ名 (){}"で呼び出せるのか。

解釈の仕方が違う気がします。
クロージャは普通名前がついたものとしては捉えません。上記でどの部分かといえば、useClosure関数の中にあるexecClosureメソッドを呼び出したときの実引数:{ print(...); print(...); print(...) }の部分です。

クロージャを呼び出している部分はexecClosureの本体部分にあるclosure()ですね。

ローカルスコープ(useClosure())内に入っていようとも外からクロージャを呼び出せるのか。

それができるのがクロージャーの存在意義です。例えば普通の例を挙げますと

swift

1 2func adder(_ a: Int) -> (Int) -> Int { 3 return { (b: Int) -> Int in 4 return a + b 5 } 6} 7 8let add2 = adder(2) 9let add4 = adder(4) 10print(add2(3)) 11print(add4(3))

adder(2)でクロージャーが返されてます。そのクロージャーはadder関数の引数を参照できるような文脈の物ですがadder関数自体の実行はadd2へ値が代入さた時点では既に完了しています。にもかかわらずprint(add2(3))でクロージャーを呼び出すとadder(2)が呼び出された時点での引数の値を参照した式a+bが計算されてその結果が計算できていますね?

複数の同名のクロージャが存在している場合(複数のローカルスコープ内にそれぞれ)どのクロージャが呼び出されるのか。

クロージャーに名前がついていると勘違いされているが故の質問だと思います。名前がついているクロージャーっていうのはもはや単なる関数(メソッド)です。


追記:クロージャーを評価する際の「文脈」の機能を表す、ありがちな例を追記してみます。

swift

1func adder(_ _a: Int) -> ((Int) -> Int, (Int) -> Void) { 2 var a = _a 3 return ( { (b: Int) -> Int in a + b }, // 複数のクロージャーで変数a 4 { (b: Int) -> Void in a = b } ) // を共有できる。 5} 6 7let (add, set) = adder(10) 8print(add(2)) // 12 9print(set(20)) // 20 10print(add(2)) // 22 <=これに注目してほしい

投稿2018/03/09 07:47

編集2018/03/09 10:31
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jantyran

2018/03/09 08:53 編集

回答ありがとうございます。 サンプルコードを見していただいて以下の様に理解しましたが、合っているでしょうか。 let add2 = adder(2) の時点では {(b: Int) -> Int return a + b } の a に 引数で渡した 2 入ったものが返されている。(この時点では関数は実行されないで、ただ返り値として渡されている) それなので、add2 の中身は {(b: Int) -> Int return 2 + b } となっている。 print(add2(3)) 時点で、 {(b: Int) -> Int return 2 + b }のb に引数3が渡されて、クロージャが実行されることで計算後の値が返り値として表示される。 また、追加の質問で失礼致しますが、 execClosureが呼ばれている時には引数= ()内に描かれておらず、外に{ print(...); print(...); print(...) }が描かれていますが、これでいいのでしょうか。 execClosure({ print(...); print(...); print(...) })が呼び出し方として正しいと思ったのですが。。。
KSwordOfHaste

2018/03/09 09:59

> add2 の中身は {(b: Int) -> Int return 2 + b } となっている。 そう解釈してもよい場合もありますが、正確には多分ちょっと違うと思います。あくまでa+bとなっていて「このaは2だよ」という文脈情報が別途含まれていると捉えた方がよいでしょう。不正確な表現ですが、クロージャーはこんな雰囲気のオブジェクトと捉えるといいかもしれません。 [ "expression": {(b: Int) -> Int return a + b }, "environment": ["a": 2]] expressionが評価されるにあたり環境としてaが2であることが一緒に考慮されるという雰囲気です。
KSwordOfHaste

2018/03/09 10:14

> execClosureが呼ばれている時には引数...内に描かれておらず... ご質問のコード上に「接尾形式でクロージャーを渡す」とコメントがあります。クロージャーを引数にする場合などこういうふうにも書けるのだと思います。自分はswiftにそれほど明るい訳ではないのですが、他の言語でも似たような文法があるのです。closureが引数になっているような場合 execClosure(closure: { (b: Int) -> Int in a + b }) とも書けますし execClosure() { (b: Int) -> Int in a + b } とも書けるのですね。 when(true) { ()->Void in ... } こんなふうな書き方ができるとシンタックスシュガー的な関数が定義できるとか、カリー化の構文にもマッチしているとかそういう理由なのかも知れません。自分が使ったことある言語だとScalaも似た構文を持ってます。つまりswift独自ではなくて一般的に好まれるなんらかの根拠がある構文だと思います。
jantyran

2018/03/12 02:31

なるほど、たいへんよくわかりました。 丁寧に教えていただき、ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問