1-6 クロージャの基本的な使用法

クロージャは、名前を持たない無名の関数のようなものです。

クロージャは、変数や引数に代入することができ、関数の戻り値としても使用できます。

また、クロージャは、無名関数(むめいかんすう)とも呼ばれます。

クロージャとか、無名関数とかって言われると身構えるけど、

関数みたいなもので、関数とちょっと書き方が違って、
関数よりスマート直感的に書くことができるものです。

つまり、クロージャとは、「関数の書き方の一種」です。

クロージャの基本構文

let クロージャの名前: () -> 返り値 = {
    // 処理
    // returnがある場合は、ReturnTypeに合わせて返り値を記述
}

関数の構文とほとんど同じですね。

それでは、関数の書き方と、クロージャの書き方を比べて見てみましょう。

是非、実際に操作しながら学んでいきましょう。
以下でSwiftのブラウザ実行環境が使えます。 print()で出力される文字を確認してみましょう。https://paiza.io/ja/projects/new

関数の構文

func person(name: String, age: Int) -> String {
    return "こんにちは \(name)です。 年齢は \(age) 歳です。"
}

// 関数の呼び出し
let message = person(name: "RIKO", age: 26)
print(message) // こんにちは RIKOです。 年齢は 26 歳です。

クロージャの構文

let person: (String, Int) -> String = { name, age in
    return "こんにちは \(name)です。 年齢は \(age) 歳です。"
}

// クロージャの呼び出し
let message = person("RIKO", 26)
print(message) // こんにちは RIKOです。 年齢は 26 歳です。

以下のようにpersonという変数を定義せずに、直接呼び出すこともできます。

let message = { (name: String, age: Int) -> String in
    return "こんにちは \(name) です。 年齢は \(age) 歳です。"
}("RIKO", 26)

print(message) // こんにちは RIKOです。 年齢は 26 歳です。

関数 と クロージャ の違い

func キーワードの有無

  • 関数は func キーワードを使って宣言されます
  • クロージャはそのようなキーワードがありません

名前の有無

  • 関数は名前を持ち、呼び出す際にはその名前を使用します
  • クロージャは無名(名前がない)か、変数や定数に代入されたときに名前がつくこともあります

簡略構文

  • クロージャでは、引数や戻り値の型を省略したり、簡略構文($0 など)を使用したりできることがあります

クロージャは、関数と形では似ていますが、クロージャは関数の特別な書き方であり、関数よりコンパクトな書き方ができるのです。

では、クロージャの書き方について更に詳しく見ていきましょう。

クロージャ の いろいろな書き方

// 完全な構文 (省略しないクロージャの構文)
let add: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
    return a + b
}
print( add(3, 4) ) // 7
// 型省略の構文 (クロージャの戻り値の型を省略)
let add: (Int, Int) -> Int = { a, b in
    return a + b
}
print( add(3, 4) ) // 7
// 色々省略した構文 (引数も戻り値も省略)
let add: (Int, Int) -> Int = { $0 + $1 }
print( add(3, 4) ) // 7

一応、関数の書き方もおさらい(クロージャじゃないよ)

func add(_ a: Int, b: Int) -> Int {
    return a + b
}
print( add(3, 4) )  // 7

上記のプログラムは、全て同じことをやっています。

難しいですよね。クロージャーで省略することを覚えると、難しいことができるようになった!と思って、使いたくなると思います。

私も、覚えたての時に、イキって

let add: (Int, Int) -> Int = { $0 + $1 }

この書き方をよく使っていましたが、
先輩から、「僕の好みですが、この書き方は、パッと見なにやってるか分かりずらいです。可読性を意識した時に、何が相応しいか見直しましょう」

というコメントをもらったことがあります。

コードをチェックしてくれるレビュアーや、今後、このコードを読み解く人のためにも、可読性を意識したコードを書きたいと思いました。

可読性 = 読みやすさ

例えば、配列を扱う際のクロージャも、以下のように省略することができます。

let numbers = [1, 2, 3, 4, 5]

// 各要素を2倍にするクロージャ
let doubled = numbers.map({ (number: Int) -> Int in
    return number * 2
})
print(doubled) // [2, 4, 6, 8, 10]

let numbers = [1, 2, 3, 4, 5]

// 型省略の構文 (クロージャの戻り値の型を省略)
let doubled = numbers.map({ number in
    return number * 2
})
print(doubled) // [2, 4, 6, 8, 10]

let numbers = [1, 2, 3, 4, 5]

// 引数・戻り値型の省略と$0の利用
let doubled = numbers.map { $0 * 2 }
print(doubled) // [2, 4, 6, 8, 10]

まとめ

  • クロージャは「無名関数」(名前を持たない関数) である
  • クロージャは、省略した書き方ができる

仕事で、ソースコードを読んでいると、クロージャがたくさん登場します。

クロージャの書き方を習得していなくても、動くプログラムは書けますが、
ソースコードを読んでいるとクロージャが頻繁に出てくるので、「こういう書き方がある!」というのは知っておくと良いと思います。

ドリル

// 完全な構文 (省略しないクロージャの構文)
let fullName: (String, String, Int) -> String = { (firstName: String, lastName: String, age: Int) -> String in
    return "私の名前は、\(lastName) \(firstName)。\(age)歳です。"
}

print(fullName("ジョブズ", "スティーブ", 56))
// 型省略の構文 (クロージャの戻り値の型を省略)

let fullName: (String, String, Int) -> String = { firstName, lastName, age  in
    return "私の名前は、\(lastName) \(firstName)。\(age)歳です。"
}

print(fullName("ジョブズ", "スティーブ", 56))
// 色々省略して$0などを利用する構文 (引数も戻り値も省略)
let fullName: (String, String, Int) -> String = { "私の名前は、\($1) \($0)。 \($2)歳です。" }

print(fullName("ジョブズ", "スティーブ", 56))

メモ