SwiftのCustomOperator概要とその使い方

アプリを作成していると、規定の演算子(+, ==, ! など)では不十分な処理を記述しなくてはならない場合があります。

例えば、Swiftでのべき乗などです。

ここでは、演算子の上書き(operator overloading)、そして作成方法について紹介していきます。

## Operator Overloadingとは
例えば、DiffableDataSourceのItemIdentifierに各CellのViewModelが入る場合には、各CellはHashableでなくてはなりません。

```swift
final class DataSource: UICollectionViewDiffableDataSource<Int, ItemIdentifier> {
    init(_ collectionView: UICollectionView) {
        super.init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell",
                                                          for: indexPath) as! CollectionViewCell
            cell.viewModel = itemIdentifier.viewModel
            return cell
        }
    }
}

// Type 'ItemIdentifier' does not conform to protocol 'Equatable' 
// Type 'ItemIdentifier' does not conform to protocol 'Hashable'
struct ItemIdentifier: Hashable {
    let viewModel: CollectionViewCellViewModel
    let id: String = UUID().uuidString
}
```
このようにEquatableやHashableに準拠したいけど、そのままだとできない場合、以下に示すような演算子の上書き(Operator Overloading)が使用されます。
```swift
struct ItemIdentifier: Hashable {
    ...
    // Equatableに準拠。元々存在する'=='をItemIdentifierで使う際の定義に上書きしている。
    static func == (lhs: ItemIdentifier,
                    rhs: ItemIdentifier) -> Bool {
        lhs.hashValue == rhs.hashValue
    }

    // Hashableに準拠するために必要な関数定義。一意にするための各種パラメータをhasher.combine()に渡す。
    func hash(into hasher: inout Hasher) {
        hasher.combine(viewModel.cellText)
    }
}
```

## Custom Operatorについて
既存の演算子にはない演算をしたい場合、自分で演算子を作成することができます。
例えば、べき乗などは多くのプログラミング言語に搭載されていますが、Swiftではまだありません。
ここではCustom Operatorの作り方について詳しく見ていきたいと思います。

### 前提
演算子には大きく分けて「単項演算子」、「二項演算子」、「三項演算子」の三種類があります。
- 単項演算子…一つの対象に付与する演算子です。`!`が代表例で、`!value`のように前側に付けば否定に、`value!`のように後ろ側につけばForce-unwrappingとして使うことができます。
- 二項演算子…二つの対象に付与する演算子です。`+, -, *, /, %, ==, <`など、二つの対象に挟まれる形で使われます。ほとんどの演算子はこれにあたります。
- 三項演算子…三つの対象に付与する演算子です。Swiftでは1行条件分岐(A ? B : C)のみがこれにあたります。

ここでは、二項演算子の作り方を紹介します。

### 実際の作り方
他の言語同様、「**」でべき乗を表現したいものとします。
まず、**が演算子と認識してもらうために、以下を記述します。
```swift
// infixは二つに挟まれる形で使用されるoperatorに使用する修飾子。
infix operator **
```

そして、実際の処理の内容をOperator Overloadingと同様に記述します。
```swift
func **(base: Int, power: Int) -> Int {
    precondition(power >= 2)
    var result = base
    for _ in 2...power {
        result *= base
    }
    return result
}
```

すると、結果は以下のようになります。
```swift
print(2 ** 2) // 4
print(2 ** 8) // 256
```

ちなみに、単項演算子を作りたい場合は以下のようにします。

```swift
// %を作る
postfix operator %

postfix func %(num: Float) -> Float {
    return num / 100
}

print(1.0%) // 0.010000
```

### 様々な型に適応させる
Intだけでなく、Int32など様々なIntegerに適用したい場合、Integer型はBinaryInteger型に準拠することを利用して、以下のように記述します。
```swift
func **<T: BinaryInteger>(base: T, power: Int) -> T {
    precondition(power >= 2)
    var result = base
    for _ in 2...power {
        result *= base
    }
    return result
}
```
また、自身をべき乗した結果に移し替えたい時、`*=`のような演算子が欲しくなります。そちらは以下のように記述できます。
```swift
// 既出の定義は省略
infix operator **=

func **=<T: BinaryInteger>(lhs: inout T, rhs: Int) {
  lhs = lhs ** rhs
}

struct CustomOperatorView: View {
    @State var currentValue = 2

    var body: some View {
        Button("exponent") {
            currentValue **= 2
        }
        Text("\(currentValue)")
    }
}
```
|一回押下時|二回押下時|
|-|-|
|![Simulator Screenshot - iPhone 15 Pro - 2024-05-16 at 11.06.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069252/f66a3987-e038-44db-be55-34e6160c89fa.png)|![Simulator Screenshot - iPhone 15 Pro - 2024-05-16 at 11.06.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069252/5e556006-c665-8af5-8242-f71d463eac14.png)
|

### 計算順について
以下のような場合を考えます。
```swift
struct CustomOperatorView: View {
    ...
    var body: some View {
        ...
        // Adjacent operators are in unordered precedence groups 'MultiplicationPrecedence' and 'DefaultPrecedence'
        Text("\(currentValue * 2 ** 3)")
    }
}
```
計算の優先順位がわからないため、コンパイルエラーが発生しています。()で囲うことによって順番は指定できますが、いちいち指定するのも面倒です。
Swiftでは、Precedence Groupという、計算順に関するグループが存在します。例えば、+には`AdditionPrecedence`が、*には`MultiplicationPrecedence`が割り当てられています。
Precedence Groupは以下のような書き方で、自分で定義することができます。
```swift
precedencegroup ExponentiationPrecedence {
    // 連続した場合、右方向に計算するか、左方向に計算するか
    associativity: right
    // Precedence Groupの立ち位置の指定
    higherThan: MultiplicationPrecedence
}
```
Precedence Groupは[こちら](https://developer.apple.com/documentation/swift/operator-declarations)に、優先度順に載っています。
(`DefaultPrecedenceGroup`は`TernaryPrecedence group`の一つ上、と出ていますね。)
## まとめ
- Operator Overloadingによって既定の演算子の定義を上書きできる。
- Custom Operatorによって既定の演算子に存在しない演算子を定義できる。
- 各演算子は`postfix`, `prefix`, `infrx`のいずれかの形で定義できる。
- 計算の優先順位を決めるため、Precedence Groupを定義する必要がある。

よく使う処理を関数よりも簡潔に書くことのできる方法なので、CustomOperatorは是非とも使いこなしたい方法です。

### 最後に
こちらは私が書籍で学んだ学習内容をアウトプットしたものです。
わかりにくい点、間違っている点等ございましたら是非ご指摘お願いいたします。