Edited at

Swiftのタプルと引数・戻り値

More than 3 years have passed since last update.

 せっかく調べたのでメモ。ただし、その前にクロージャについてメモ。


クロージャ

 抽象的な文法はこちら。

{ (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における引数名の仕様は、外部引数名・ローカル引数名というポリシーの問題ばかりでなく、「名前なしのタプルが引数か、それとも (部分的あるいは全面的に) 名前付きのタプルが引数か」という引数のデータ型の問題として捉えてもいいかもしれません。

 なお変数を引数にできない理由は今のところ不明です。