Swift4.0でカスタムオペレーターを作成してみる
はじめに
カスタムオペレーターとは+
や-
の四則演算に加えて新たに自分で演算子を定義できるものです。
特に以下の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
これは優先度の話です。
+
や-
は×
などと比べると優先度が低いと言えます。
このような優先度を定義しているわけです。
演算子のオーバーロード
オーバーライドする方法があります。
オーバーロードとオーバーライドは大きく異なるので注意が必要です。
まとめ
必要になったら定義してよみやすいコードを書いていければと思います。