Kekeの日記

エンジニア、読書なんでも

Swift4.0でカスタムオペレーターを作成してみる

https://developer.apple.com/swift/images/swift-og.png

はじめに

カスタムオペレーターとは+-の四則演算に加えて新たに自分で演算子を定義できるものです。

特に以下のGPUに関するフレームワークでは

camera --> filter --> renderView

のような演算子が使われています。

なぜ使うのか

一般的に可読性の観点だと考えることができます。

特に関数型プログラムは、パイプライン演算子と呼ばれるような演算子があるおかげで非常に簡潔に書くことができます。

定義の仕方

まずは低い優先度で定義します。

precedencegroup Base {
    associativity: left
    lowerThan: AdditionPrecedence
}

そしてinfixという、日本語直訳では取り付けっといったような意味をもつものでオペレータを定義します。

infix operator <+> : Base
infix operator <*> : Base

そして、その関数が一体どのような関数であるかを定義します。

以下のような感じです。

public func <+> (lhs: Int, rhs: Int) -> Int {
    print("<+>: called `Base`")

    return lhs + rhs
}

public func <*> (lhs: Int, rhs: Int) -> Int {
    print("<*>: called `Base`")

    return lhs * rhs
}

Playgroundでも確認することができました。

<+>: called `Base`
<*>: called `Base`
20

構造体やについても同様のことをすることができて、ObjectiveーCのようにメンバにアクセスすることができます。

ただし注意が必要でinoutをつけることによって参照型を渡す必要があります。

これはGolangやObjective-Cでも、ポインタを渡す必要があるのと一緒です。

まずはクラスを定義します。

public class User {
    var name = ""
}

そして演算子を適当に定義します。

infix operator -> : Base

public func -> (lhs: inout User, rhs: String) -> Void {
    lhs.name = rhs
}

そして実際に使用します。

var user = User()
let name = "Test"
user->name
print(user.name)

// => Test

Objective-Cとの比較

以下のようにObjective-Cではメンバにアクセスします。

User *user = [[ User alloc ] init];

tarou->name = @"KeisukeYamashita";

今回のカスタム演算子を使うと

var user = User()
user->"KeisukeYamashita

Swiftはオーバーロードを定義することができるので使ってみるといいです。

そのときは明示的にclass funcとキーワードをつけてからやるのがいいと思います。

オーバーロードしないのならstaticをつけるべきです。

エラーハンドル付きカスタム演算子

以下のようにもちろん例外処理もすることができます。

エラー処理をすることを明言するためにthrowsを付け加えます。

func -> (lhs: Int, rhs: Int) throw -> Int {
    if ... {
        // ...
     }
}

そして使うときに

do {
    try 1 -> 2
} catch {
    // ...
}

とすることができます。

結合と優先度

最初に以下のように定義しました。

precedencegroup Base {
    associativity: left
    lowerThan: AdditionPrecedence
}

associativity

数学でいう結合性のことです。 線形代数の初歩ですね。

1 + 2 + 3 が ( 1 + 2 ) + 3と解釈されるのが左結合で、1 + ( 2 + 3 )と解釈されるのが右結合です。

特に行列演算などは交換可能ではないので、注意が必要です。

lowerThan

これは優先度の話です。

+-×などと比べると優先度が低いと言えます。

このような優先度を定義しているわけです。

演算子のオーバーロード

オーバーライドする方法があります。

オーバーロードとオーバーライドは大きく異なるので注意が必要です。

まとめ

必要になったら定義してよみやすいコードを書いていければと思います。

参考文献

qiita.com

medium.com

www.studiogalago.com