Menu

Category

Archive

logo


The Swift Programming Language ~ Advanced Operators

2014-07-01 13:00:00 +0900
  • このエントリーをはてなブックマークに追加

iBooks にある "The Swift Programming Language" の勉強メモ。Objective-C と C を普段書いている自分から、ちょっと馴染みがないものを特にまとめておきます。目次は こちら

今回は、Advanced Operators に関して。

Advanced Operators

今まで見てきたような基本的なオペレーター以外に、Swift ではビット操作等のオペレーターも C 同様にサポートしています。また、既存のオペレーターをオーバーライドすることも可能です。さらに、独自のオペレーターを作成することもできます。

1. ビット操作オペレーター

C と同じように Swift でも、ビット操作をすることができます。基本的にシンタックスも同じようです。

以前、C のビット操作に関する記事を書いたので、詳しくは こちらこちら へ。iBooks のマニュアルには、sign integer や unsign integer のビットレベルでの表し方等、基本的なことも細かく解説されていました。

 1 // NOT
 2 let initialBits: UInt8 = 0b00001111
 3 let invertedBits = ~initialBits  // equals 11110000
 4 // AND
 5 let firstSixBits: UInt8 = 0b11111100
 6 let lastSixBits: UInt8  = 0b00111111
 7 let middleFourBits = firstSixBits & lastSixBits  // equals 00111100
 8 // OR
 9 let someBits: UInt8 = 0b10110010
10 let moreBits: UInt8 = 0b01011110
11 let combinedbits = someBits | moreBits  // equals 11111110
12 // XOR
13 let firstBits: UInt8 = 0b00010100
14 let otherBits: UInt8 = 0b00000101
15 let outputBits = firstBits ^ otherBits  // equals 00010001
16 // Left and Right Shift
17 let shiftBits: UInt8 = 4   // 00000100 in binary
18 shiftBits << 1             // 00001000
19 shiftBits << 2             // 00010000
20 shiftBits << 5             // 10000000
21 shiftBits << 6             // 00000000
22 shiftBits >> 2             // 00000001
23 // Shift Operator with Hex Color Code
24 let pink: UInt32 = 0xCC6699
25 let redComponent = (pink & 0xFF0000) >> 16    // redComponent is 0xCC, or 204
26 let greenComponent = (pink & 0x00FF00) >> 8   // greenComponent is 0x66, or 102
27 let blueComponent = pink & 0x0000FF           // blueComponent is 0x99, or 153

2. オーバーフローオペレーター

Swift では、オーバーフロー・アンダーフローともに発生した場合には、エラーを吐きます。意図しない間違った値を使用してしまうことを防いでくれます。

1 var potentialOverflow = Int16.max
2 // potentialOverflow equals 32767, which is the largest value an Int16 can hold
3 potentialOverflow += 1
4 // this causes an error

エラーを起こすのではなく、通常のようにオーバーフロー・アンダーフローさせたい場合には、その値を丸めてくれるようにすることもできます。その際には、オペレーターの前に & をつけます。

  • ・オーバーフロー足し算 : &+
  • ・オーバーフロー引き算 : &-
  • ・オーバーフロー掛け算 : &*
  • ・オーバーフロー割り算 : &/
  • ・オーバーフロー 余り : &%

例を見てみます。

1 var willOverflow = UInt8.max
2 // willOverflow equals 255, which is the largest value a UInt8 can hold
3 willOverflow = willOverflow &+ 1
4 // willOverflow is now equal to 0
5 
6 var willUnderflow = UInt8.min
7 // willUnderflow equals 0, which is the smallest value a UInt8 can hold
8 willUnderflow = willUnderflow &- 1
9 // willUnderflow is now equal to 255

また、0 で割った場合と 0 で余りを求めた場合に、エラーを吐くのではなく、0 を返すという効果もあります。

1 let x = 1
2 let y = x &/ 0
3 // y is equal to 0

3. オペレーター関数

クラスと構造体は、すでに存在しているオペレーターに独自の実装を追加することができます。これをオーバーライドと言います。

下記の例では、Vector2D という独自の構造体に対して、新しい + オペレーターをオーバーライドしています。

 1 struct Vector2D {
 2     var x = 0.0, y = 0.0
 3 }
 4 @infix func + (left: Vector2D, right: Vector2D) -> Vector2D {
 5     return Vector2D(x: left.x + right.x, y: left.y + right.y)
 6 }
 7 
 8 let vector = Vector2D(x: 3.0, y: 1.0)
 9 let anotherVector = Vector2D(x: 2.0, y: 4.0)
10 let combinedVector = vector + anotherVector
11 // combinedVector is a Vector2D instance with values of (5.0, 5.0)

この @infix 属性は、binary operator (オペランドが 2 つあるオペレーター) に対して使用します。このオペレーター関数は、グローバル関数なので、2 つのインスタンスに上記のように使用できます。

また、unary operator も定義できます。この際には、@postfix@prefix 属性を使用します。

1 @prefix func - (vector: Vector2D) -> Vector2D {
2     return Vector2D(x: -vector.x, y: -vector.y)
3 }
4 
5 let positive = Vector2D(x: 3.0, y: 4.0)
6 let negative = -positive
7 // negative is a Vector2D instance with values of (-3.0, -4.0)
8 let alsoPositive = -negative
9 // alsoPositive is a Vector2D instance with values of (3.0, 4.0)

また、Compound assignment operators (+= とか /= とか) もオーバーライドできます。その際には、@assignment を使用します。また、この Compound assignment operators のオペレーター関数を書く際には、左の引数に inout キーワードを使用しなければなりません。この引数は変更され、この変更は関数呼び出し後も持続なければならないからです。

1 @assignment func += (inout left: Vector2D, right: Vector2D) {
2     left = left + right
3 }
4 
5 var original = Vector2D(x: 1.0, y: 2.0)
6 let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
7 original += vectorToAdd
8 // original now has values of (4.0, 6.0)

先ほど、Vector2D のために + をオーバーライドしているので、上記のように簡潔に書くことができます。

@assignment@postfix@prefix を組み合わせることもできます。

1 @prefix @assignment func ++ (inout vector: Vector2D) -> Vector2D {
2     vector += Vector2D(x: 1.0, y: 1.0)
3     return vector
4 }
5 
6 var toIncrement = Vector2D(x: 3.0, y: 4.0)
7 let afterIncrement = ++toIncrement
8 // toIncrement now has values of (4.0, 5.0)
9 // afterIncrement also has values of (4.0, 5.0)

いくつかオペレーターをオーバーライドする例をみてきましたが、= はオーバーライドすることができません。また、ternary condicional operator (ex. (a ? b : c)) は、オーバーライドできません。

独自のクラス・構造体は、比較オペレーターをデフォルトでは持ちません。Swift 側から推測することはできません。先ほどと同じように実装することで、比較オペレーターを使用することができるようになります。

 1 @infix func == (left: Vector2D, right: Vector2D) -> Bool {
 2     return (left.x == right.x) && (left.y == right.y)
 3 }
 4 @infix func != (left: Vector2D, right: Vector2D) -> Bool {
 5     return !(left == right)
 6 }
 7 
 8 let twoThree = Vector2D(x: 2.0, y: 3.0)
 9 let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
10 if twoThree == anotherTwoThree {
11     println("These two vectors are equivalent.")
12 }
13 // prints "These two vectors are equivalent."

4. カスタムオペレーター

Swift では、カスタムオペレーターを定義することができます。これには、/ = - + * % < > ! & | ^ . ~ を使用することができます。operator キーワードを使用し、グローバルに宣言されます。

1 operator prefix +++ {}

そして、宣言した後は、実際に実装をしていきます。先ほどと同じ Vector2D に対して、この独自のオペレーターを追加する例をみていきます。

1 @prefix @assignment func +++ (inout vector: Vector2D) -> Vector2D {
2     vector += vector
3     return vector
4 }
5 
6 var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
7 let afterDoubling = +++toBeDoubled
8 // toBeDoubled now has values of (2.0, 8.0)
9 // afterDoubling also has values of (2.0, 8.0)

独自の infix オペレーターは独自の precedence と associativity を指定できます。precedence とは、オペレーター同士の優先順です。associativity とは、同じ優先順位を持ったオペレーターがどのようにくっつくか(右から or 左から or 同じ優先順位のオペレーターを一緒になれない)を決める要因です。

associativity は、leftrightnone を指定でき、デフォルトは、none です。precedence は数字で優先順位を定義します。デフォルトは、100 です。

1 operator infix +- { associativity left precedence 140 }
2 func +- (left: Vector2D, right: Vector2D) -> Vector2D {
3     return Vector2D(x: left.x + right.x, y: left.y - right.y)
4 }
5 let firstVector = Vector2D(x: 1.0, y: 2.0)
6 let secondVector = Vector2D(x: 3.0, y: 4.0)
7 let plusMinusVector = firstVector +- secondVector
8 // plusMinusVector is a Vector2D instance with values of (4.0, -2.0)