[Swift]「クロージャー」

クロージャーについて、基本文法、使い方、関数との比較、関数からクロージャーへの変換などについて解説します。

目次

クロージャーの基本

クロージャーの基本的な概念

クロージャーは、関数として扱える無名のブロックです。

これは、関数内で定義され、関数内の変数や定数を参照することができます。クロージャーは、自己完結型であり、外部の状態に依存せずに実行されます。この特性により、柔軟なコードの記述や再利用性の向上が可能となります。

基本文法と使い方

クロージャーの基本文法具体的な使い方について解説します。

STEP
基本文法:クロージャーの宣言方法
{ (parameters) -> ReturnType in
    // クロージャーの処理
}
  • {}:
    クロージャーは中括弧 {} で囲まれます。
  • (parameters):
    クロージャーの引数が含まれる部分です。引数がない場合は空のカッコ () を使用します。引数が複数ある場合はカンマ , で区切って列挙します。
  • -> ReturnType:
    クロージャーの戻り値の型を指定します。戻り値がない場合は省略可能です。
  • in:
    クロージャーの引数と処理本体を分けるために使用されます。
STEP
引数と戻り値の扱い方
let addNumbers = 
    { (num1: Int, num2: Int) -> Int in
    return num1 + num2
}

let result = addNumbers(5, 3)
print(result) 
// 出力結果:8
  1. addNumbers というクロージャーを定義しています。
  2. 引数として num1num2 を受け取り、それらを足し合わせています。
  3. 戻り値の型は Int です。
  4. クロージャーを実行する際は、関数と同じように引数を渡して呼び出します。

文法のポイント

クロージャーの文法において注意すべきポイントがあります。

STEP
引数や戻り値の型推論

引数や戻り値の型を省略することができます。Swiftの型推論機能が働き、自動的に型を判断してくれます。

let addNumbers = { a, b in
    return a + b
}
  1. このクロージャーは、2つの引数 ab を受け取り、その和を返すものです。
  2. しかし、引数や戻り値の型を明示的に宣言していません
  3. Swiftの型推論機能により、コンパイラは自動的に型を推論します。
  4. この場合、引数 ab整数型であり、戻り値も整数型であることを推論します。

このように、引数や戻り値の型を省略することでコードがシンプルになります。ただし、型推論がうまく働かない場合や明示的な型宣言が必要な場合もありますので、注意が必要です。

STEP
短縮形の引数名

引数名が不要な場合は、短縮形の引数名 $0, $1, $2, … を使用することができます。

let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map({ $0 * 2 })
  1. map 関数を使って numbers 配列の要素を2倍にしています。
  2. 引数名が不要なため、$0 と書くことで配列の要素にアクセスしています。
  3. このように短縮形の引数名を使うことで、コードをよりシンプルにすることができます。

クロージャーと関数を比較

クロージャー関数と似たような役割を果たすため、それらの違いを理解することは重要です。

主な特徴を比較

まず、クロージャーと関数の主な特徴について比較してみました。

項目クロージャー関数
定義と宣言変数や定数に代入して使用するか、即時実行することができる。関数名を宣言してから呼び出すことができる。
キャプチャリング外部スコープの変数や定数キャプチャして使用できる。キャプチャリングの概念はない。
シンタックスシュガーの活用短縮形の引数名$0などを活用して、コードを簡潔に記述できる。活用は限られており、通常の関数構文を使用する。
クロージャー vs 関数

キャプチャリングは、クロージャーが外部スコープの変数や定数へのアクセスを可能にする機能です。

シンタックスシュガー

短縮形の引数名:
引数名が不要な場合、$0$1$2 のような短縮形の引数名を使用できます。これにより、引数の名前を省略してコンパクトなコードを書くことができます。

let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map({ $0 * 2 })

上記の例では、map 関数内で短縮形の引数名 $0 を使用して配列の要素にアクセスしています。

コード事例を使って比較

事例
数値の合計を計算する

数値の合計を計算するというシンプルなタスクをクロージャーと関数の両方で実装してみましょう。

1.関数

func calcSum(numbers: [Int]) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}

let numbers = [1, 2, 3, 4, 5]
let sum = calcSum(numbers: numbers)
print(sum) 

//出力結果:15

2.クロージャーreduce(0)

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

// reduceメソッドを使用して配列の要素を合計する
let sum = numbers.reduce(0) { (result, number) in
    // result: 現在までの累積された値
    // number: 配列内の現在の要素
    // クロージャー内で result と number を使って計算し、新しい累積値を返す

    return result + number
}
print(sum) 

//出力結果:15
  • このコードは、reduce メソッドを使用して配列 numbers 内の要素を合計する例です。
  • reduce メソッドのクロージャー内で、引数 result は現在までの累積された値を、引数 number は配列内の現在の要素を表します。
  • クロージャー内で resultnumber を使って計算し、新しい累積値を返すことで、reduce メソッドは最終的な合計値を計算します。
事例
文字列のフィルタリング

1.関数

func filterFruits(fruits: [String]) -> [String] {
    var filteredFruits = [String]()
    for fruit in fruits {
        if fruit.count > 5 {
            filteredFruits.append(fruit)
        }
    }
    return filteredFruits
}

let fruits = ["apple", "banana", "cherry", "grape", "orange"]
let filteredFruits = filterFruits(fruits: fruits)
print(filteredFruits) 
// 出力結果:["banana", "cherry", "orange"]

2.関数

// 配列の定義
let fruits = ["apple", "banana", "cherry", "grape", "orange"]

// filterメソッドを使用して文字数が5より大きい要素を抽出
let filteredFruits = fruits.filter { fruit in
    // クロージャー内で各要素を処理
    // fruit.count > 5 という条件を満たす要素を抽出
    return fruit.count > 5
}

// 条件を満たす要素を出力
print(filteredFruits) 
// 出力結果: ["banana", "cherry", "orange"]
  • このコードは、filter メソッドを使用して、配列 fruits 内の要素から特定の条件を満たす要素を抽出する例です。
  • filter メソッドのクロージャー内では、引数 fruit が配列内の各要素を指します。
  • クロージャー内の条件 fruit.count > 5 は、各要素の文字数が5より大きいかどうかを判定しています。
  • 条件を満たす要素だけが選択され、その要素だけで新しい配列 filteredFruits が作成されます。
  • 最終的に、print(filteredFruits) で条件を満たす要素を出力しています。
  • この例では、条件を満たす要素は “banana”、”cherry”、”orange” の3つであり、それが出力されます。

関数をクロージャーへ変換

STEP
関数の基本形 → クロージャーへ変換

関数の基本形をクロージャーに変換してみましょう。

//関数の基本形
func sum (firstNumber : Int, 
    secondNumber : Int) -> Int {
    return firstNumber + secondNumber
}
  1. fuc sumをとる ←無名関数
  2. { を先頭に移動
  3. { のあった場所に inを付ける
//クロージャー
{ (firstNumber : Int, secondNumber : Int) -> Int In
    return firstNumber + secondNumber
}
STEP
multiply関数 → クロージャーへ変換

今度は、さらに理解を深めるためにmultiply関数を使って、同じことをやってみましょう。

func calculator (n1: Int, n2: Int, 
    operation: (Int, Int) -> Int {
    return operation(n1, n2)
}

func multiply(no1: Int, no2: Int) -> Int {
    return no1 * no2
}

calculator(n1: 2, n2: 3, 
    operation: multiply)
  1. fuc sumをとる ←無名関数
  2. { を先頭に移動
  3. { のあった場所に inを付ける
  4. calculatorの中にあるmultiplyに代入する
func calculator (n1: Int, n2: Int, 
    operation: (Int, Int) -> Int {
    return operation(n1, n2)
}

calculator(n1: 2, n2: 3, 
    operation: {(no1: Int, no2: Int) -> Int In
    return no1 * no2
})
  • Swiftは型推論ができるので、no1,no2のIntは不要
  • 同様に、Swiftは型推論ができるので、-> Intは不要
  • クロージャの中は何かを返すことが予想されるので、returnは不要
  • 1行にする
func calculator (n1: Int, n2: Int, 
    operation: (Int, Int) -> Int {
    return operation(n1, n2)
}

calculator(n1: 2, n2: 3, operation: {(no1, no2) In no1 * no2})
  1. Swiftは、第1パラメータは$0、第2パラメータは$1に変更できる
func calculator (n1: Int, n2: Int, 
    operation: (Int, Int) -> Int {
    return operation(n1, n2)
}

calculator(n1: 2, n2: 3, operation: {$0 * $1})
  • Swiftは、最後のパラメータがクロージャーのとき、パラメーター名を省略して、クロージャー{}を外に出すことができる
func calculator (n1: Int, n2: Int, 
    operation: (Int, Int) -> Int {
    return operation(n1, n2)
}

calculator(n1: 2, n2: 3) {$0 * $1}

関数をクロージャーに変換すると、かなり簡略化できますね。

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次