iBooks にある “The Swift Programming Language” の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら。
今回は、Advanced Operators に関して。
Advanced Operators
今まで見てきたような基本的なオペレーター以外に、Swift ではビット操作等のオペレーターも C 同様にサポートしています。また、既存のオペレーターをオーバーライドすることも可能です。さらに、独自のオペレーターを作成することもできます。
1. ビット操作オペレーター
C と同じように Swift でも、ビット操作をすることができます。基本的にシンタックスも同じようです。
以前、C のビット操作に関する記事を書いたので、詳しくは こちら と こちら へ。iBooks のマニュアルには、sign integer や unsign integer のビットレベルでの表し方等、基本的なことも細かく解説されていました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // NOT let initialBits: UInt8 = 0b00001111 let invertedBits = ~initialBits // equals 11110000 // AND let firstSixBits: UInt8 = 0b11111100 let lastSixBits: UInt8 = 0b00111111 let middleFourBits = firstSixBits & lastSixBits // equals 00111100 // OR let someBits: UInt8 = 0b10110010 let moreBits: UInt8 = 0b01011110 let combinedbits = someBits | moreBits // equals 11111110 // XOR let firstBits: UInt8 = 0b00010100 let otherBits: UInt8 = 0b00000101 let outputBits = firstBits ^ otherBits // equals 00010001 // Left and Right Shift let shiftBits: UInt8 = 4 // 00000100 in binary shiftBits << 1 // 00001000 shiftBits << 2 // 00010000 shiftBits << 5 // 10000000 shiftBits << 6 // 00000000 shiftBits >> 2 // 00000001 // Shift Operator with Hex Color Code let pink: UInt32 = 0xCC6699 let redComponent = (pink & 0xFF0000) >> 16 // redComponent is 0xCC, or 204 let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent is 0x66, or 102 let blueComponent = pink & 0x0000FF // blueComponent is 0x99, or 153 |
2. オーバーフローオペレーター
Swift では、オーバーフロー・アンダーフローともに発生した場合には、エラーを吐きます。意図しない間違った値を使用してしまうことを防いでくれます。
1 2 3 4 | var potentialOverflow = Int16.max // potentialOverflow equals 32767, which is the largest value an Int16 can hold potentialOverflow += 1 // this causes an error |
エラーを起こすのではなく、通常のようにオーバーフロー・アンダーフローさせたい場合には、その値を丸めてくれるようにすることもできます。その際には、オペレーターの前に &
をつけます。
- ・オーバーフロー足し算 :
&+
- ・オーバーフロー引き算 :
&-
- ・オーバーフロー掛け算 :
&*
- ・オーバーフロー割り算 :
&/
- ・オーバーフロー 余り :
&%
例を見てみます。
1 2 3 4 5 6 7 8 9 | var willOverflow = UInt8.max // willOverflow equals 255, which is the largest value a UInt8 can hold willOverflow = willOverflow &+ 1 // willOverflow is now equal to 0 var willUnderflow = UInt8.min // willUnderflow equals 0, which is the smallest value a UInt8 can hold willUnderflow = willUnderflow &- 1 // willUnderflow is now equal to 255 |
また、0 で割った場合と 0 で余りを求めた場合に、エラーを吐くのではなく、0 を返すという効果もあります。
1 2 3 | let x = 1 let y = x &/ 0 // y is equal to 0 |
3. オペレーター関数
クラスと構造体は、すでに存在しているオペレーターに独自の実装を追加することができます。これをオーバーライドと言います。
下記の例では、Vector2D という独自の構造体に対して、新しい + オペレーターをオーバーライドしています。
1 2 3 4 5 6 7 8 9 10 11 | struct Vector2D { var x = 0.0, y = 0.0 } @infix func + (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y + right.y) } let vector = Vector2D(x: 3.0, y: 1.0) let anotherVector = Vector2D(x: 2.0, y: 4.0) let combinedVector = vector + anotherVector // combinedVector is a Vector2D instance with values of (5.0, 5.0) |
この @infix
属性は、binary operator (オペランドが 2 つあるオペレーター) に対して使用します。このオペレーター関数は、グローバル関数なので、2 つのインスタンスに上記のように使用できます。
また、unary operator も定義できます。この際には、@postfix
と @prefix
属性を使用します。
1 2 3 4 5 6 7 8 9 | @prefix func - (vector: Vector2D) -> Vector2D { return Vector2D(x: -vector.x, y: -vector.y) } let positive = Vector2D(x: 3.0, y: 4.0) let negative = -positive // negative is a Vector2D instance with values of (-3.0, -4.0) let alsoPositive = -negative // alsoPositive is a Vector2D instance with values of (3.0, 4.0) |
また、Compound assignment operators (+= とか /= とか) もオーバーライドできます。その際には、@assignment
を使用します。また、この Compound assignment operators のオペレーター関数を書く際には、左の引数に inout
キーワードを使用しなければなりません。この引数は変更され、この変更は関数呼び出し後も持続なければならないからです。
1 2 3 4 5 6 7 8 | @assignment func += (inout left: Vector2D, right: Vector2D) { left = left + right } var original = Vector2D(x: 1.0, y: 2.0) let vectorToAdd = Vector2D(x: 3.0, y: 4.0) original += vectorToAdd // original now has values of (4.0, 6.0) |
先ほど、Vector2D のために + をオーバーライドしているので、上記のように簡潔に書くことができます。
@assignment
と @postfix
か @prefix
を組み合わせることもできます。
1 2 3 4 5 6 7 8 9 | @prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D { vector += Vector2D(x: 1.0, y: 1.0) return vector } var toIncrement = Vector2D(x: 3.0, y: 4.0) let afterIncrement = ++toIncrement // toIncrement now has values of (4.0, 5.0) // afterIncrement also has values of (4.0, 5.0) |
いくつかオペレーターをオーバーライドする例をみてきましたが、= はオーバーライドすることができません。また、ternary condicional operator (ex. (a ? b : c)) は、オーバーライドできません。
独自のクラス・構造体は、比較オペレーターをデフォルトでは持ちません。Swift 側から推測することはできません。先ほどと同じように実装することで、比較オペレーターを使用することができるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 | @infix func == (left: Vector2D, right: Vector2D) -> Bool { return (left.x == right.x) && (left.y == right.y) } @infix func != (left: Vector2D, right: Vector2D) -> Bool { return !(left == right) } let twoThree = Vector2D(x: 2.0, y: 3.0) let anotherTwoThree = Vector2D(x: 2.0, y: 3.0) if twoThree == anotherTwoThree { println("These two vectors are equivalent.") } // prints "These two vectors are equivalent." |
4. カスタムオペレーター
Swift では、カスタムオペレーターを定義することができます。これには、/ = - + * % < > ! & | ^ . ~ を使用することができます。operator キーワードを使用し、グローバルに宣言されます。 |
1 | operator prefix +++ {} |
そして、宣言した後は、実際に実装をしていきます。先ほどと同じ Vector2D に対して、この独自のオペレーターを追加する例をみていきます。
1 2 3 4 5 6 7 8 9 | @prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D { vector += vector return vector } var toBeDoubled = Vector2D(x: 1.0, y: 4.0) let afterDoubling = +++toBeDoubled // toBeDoubled now has values of (2.0, 8.0) // afterDoubling also has values of (2.0, 8.0) |
独自の infix オペレーターは独自の precedence と associativity を指定できます。precedence とは、オペレーター同士の優先順です。associativity とは、同じ優先順位を持ったオペレーターがどのようにくっつくか(右から or 左から or 同じ優先順位のオペレーターを一緒になれない)を決める要因です。
associativity は、left
、right
、none
を指定でき、デフォルトは、none
です。precedence は数字で優先順位を定義します。デフォルトは、100 です。
1 2 3 4 5 6 7 8 | operator infix +- { associativity left precedence 140 } func +- (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y - right.y) } let firstVector = Vector2D(x: 1.0, y: 2.0) let secondVector = Vector2D(x: 3.0, y: 4.0) let plusMinusVector = firstVector +- secondVector // plusMinusVector is a Vector2D instance with values of (4.0, -2.0) |