せっかく調べたのでメモ。ただし、その前にクロージャについてメモ。
クロージャ
抽象的な文法はこちら。
{ (parameters) -> return type in
statements
}
省略方法は色々。
// もっとも長い
{ (m: Int, n: Int) -> Int in
return m + n
}
// 引数・戻り値の型を省略
{ m, n in return m + n }
// returnを省略
{ m, n in m + n }
// 識別子すら省略
{ $0 + $1 }
// 何もかも省略、演算子のみ
[1,2,3].reduce(0, +)
クロージャの代入
基本的には、そのまま代入すればOK (単なる演算子は除く)。
let x = { (m: Int, n: Int) -> Int in
return m + n
}
データ型が推論できない場合でも、引数・戻り値を明示すれば省略を使えます。
let x: (Int, Int) -> Int = { $0 + $1 }
個人的には、こういう書き方のほうが分かりやすい気がします。
ちなみに併用もできます (詳細は後述)。
let x: (Int, Int) -> Int = {
(m: Int, n: Int) -> Int in
return m + n
}
タプル
タプルを書くのは括弧で挟むだけ。簡単。
let n = (100, 200)
名前付きタプルにするのも名前を書くだけ。簡単。
使うときには n.0
でも n.weight
でも通ります。
let n = (weight: 100, height: 200)
部分的に名前付きタプルにするのも簡単。
let n = (100, height: 200)
引数としてのタプル
※ よく分からないまま書いています
※※ Playgroundで試しています
※※※ varで宣言すると引数にできません
「Swiftにおける名前付き引数」と「関数引数としてのタプル」の2つを突き合わせながら試行錯誤したところ、引数の仕様は次のようになっているようです。
関数
関数の引数は、原則として名前なしのタプルです。
func testFunc(hoge: Int, fuga: Int) -> Int {
return hoge + fuga
}
let n = (1, 2)
testFunc(n)
引数を名前付きにすると、タプルも名前付きにしなければなりません。
func testFunc(#hoge: Int, #fuga: Int) -> Int {
return hoge + fuga
}
let n = (hoge: 1, fuga: 2)
testFunc(n)
部分的に名前付き引数にしても同様です。
func testFunc(hoge: Int, #fuga: Int) -> Int {
return hoge + fuga
}
let n = (1, fuga: 2)
testFunc(n)
クロージャ
クロージャの引数も、原則として名前なしのタプルです。
let testClosure = {
(hoge: Int, fuga: Int) -> Int in
return hoge + fuga
}
let n = (1, 2)
testClosure(n)
しかし、明示的に「名前付き引数のクロージャ」として宣言すると、名前付きタプルが引数になります。部分的に名前付き引数にしても同様です。
let testClosure: (Int, fuga: Int) -> Int = { $0 + $1 }
let m = (1, fuga: 2)
testClosure(m)
これを利用すると、メンバー関数のような使い方のクロージャも作れます。
let addNumber: (Int, with: Int) -> Int = {
x, y in
return x + y
}
addNumber(1, with: 2)
書き方が違うだけで、関数の挙動とよく似ています。“Functions are actually a special case of closures” という意味が分かるような気がします。
メンバー関数
原則として、「2つ目以降が名前付きになっているタプル」が引数です。
この例で言えば、(Int, Int)
ではなく (Int, fuga: Int)
が引数です。
class TestClass {
func testFunc(hoge: Int, fuga: Int) -> Int {
return hoge + fuga
}
}
let test = TestClass()
let n = (1, fuga: 2)
test.testFunc(n)
戻り値としてのタプル
Swiftでは、戻り値をタプルにできます。
func testFunc() -> (Int, Int) {
return (10, 20)
}
使い道がピンときませんが、戻り値を名前付きタプルにすることもできます。
func testFunc() -> (name: String, age: Int) {
return ("John", 20)
}
雑記
Swiftにおいて、括弧で挟まれた記述はみんなタプルです。たとえばメンバー関数を使うために test.testFunc(1, fuga: 2)
と書くのは、部分的に名前付きのタプル (1, fuga: 2)
を引数として渡すのと同じであり、実際そのように書くことができます。
つまり、(Int, Int)
ではなく (Int, fuga: Int)
という「部分的に名前付きのタプル」がひとつの型・ひとつの引数として処理されているようです (たぶん)。
Swiftにおける引数名の仕様は、外部引数名・ローカル引数名というポリシーの問題ばかりでなく、「名前なしのタプルが引数か、それとも (部分的あるいは全面的に) 名前付きのタプルが引数か」という引数のデータ型の問題として捉えてもいいかもしれません。
なお変数を引数にできない理由は今のところ不明です。