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

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

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