【Swift】Appleの新言語「Swift」のリファレンスを読む(18) - Advanced Operators
- (1) - The Basic
- (2) - Basic Operators、Strings and Characters
- (3) - Collection Types
- (4) - Control Flow
- (5) - Functions
- (6) - Closures、Enumerations
- (7) - Classes and Structures
- (8) - Properties
- (9) - Methods、Subscripts
- (10) - Inheritance
- (11) - Initialization
- (12) - Deinitialization、Automatic Reference Counting
- (13) - Optional Chaining
- (14) - Type Casting、Nested Types
- (15) - Extensions
- (16) - Protocols
- (17) - Generics
- (18) - Advanced Operators
- (19) - Dynamic Type、Attribute、Special Literals
注意
- あくまでメモ書きなので細かい部分を端折りますし、色々間違ってるかもしれません。ちゃんとした内容は原文を読んでね。
- コード例は基本的に原文からそのまま引用していますが、ちょっとした注釈をつけたり、統合したりしています。
SyntaxHighlighterが対応してないので微妙に読みにくいです。SyntaxHighlighterはこちらのものを使用させて頂いてます。- 他言語にそっくりな部分でも指摘しない。(自戒)
Advanced Operators(応用演算子)
ようやく最後の章…なんですが、Language Referenceの方にしか説明がないものがちらほら見つかっているので、うーんって感じです。
具体的にはdynamicType、Attribute、Literal Expression(__FILE__とか)あたりです。他にも紹介されてない構文とかキーワード(nonmutatingとかunowned(safe|unsafe))とかちょいちょいあるんですが、左記の三つを一つの記事にまとめて終わり、ってことで〆ようと思っています。
まぁそれはともかくとして、今回はBasic Operatorsの説明では挙げなかったビット演算系の演算子の説明やオーバーフロー演算子、演算子のオーバーロードの説明をしていきます。
Bitwise Operators
原文に載っている図が中々わかりやすいので、そちらを見ながらどうぞ。
とは言え、ビット演算のやり方が知りたいような人にとっては見なくてもわかるのかもしれないですが。
まずは論理否定(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
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
勿論シフト演算も可能です。≪で左シフト、≫で右シフトです。
let shiftBits: UInt8 = 4 // 00000100 in binary shiftBits << 1 // 00001000 shiftBits << 2 // 00010000 shiftBits << 5 // 10000000 shiftBits << 6 // 00000000 shiftBits >> 2 // 00000001 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
このセクションの一番下に符号ビットがどうのこうのとかそう言う話もありますが、日本語で書いてあっても解説できる自信がないので興味がある方はご自分でお読みください…って言うかそもそもこんなブログの記事なんか読まないで普通に原文読んでると思うんですが、どうでしょう。
Overflow Operators
Swiftには意図的にオーバーフローを引き起こす演算子が用意されています。
以下のコードを見てみましょう。
var potentialOverflow = Int16.max // potentialOverflow equals 32767, which is the largest value an Int16 can hold potentialOverflow += 1 // this causes an error
コメントにも書いてある通り、Int16.maxに1を足したらエラーになります。まぁそりゃそうだって感じです。
が、+の前にアンパサンドをつけるとエラーになりません。
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
Int16.maxに対して同じことをやったら-32768になります。
このようにアンパサンドを前につけた四則演算(と%)用の演算子のことをオーバーフロー演算子と呼びます。そのまんまですね。
// アンダーフローも起こせる 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 // Zero Divideも出来てしまう let x = 1 let y = x &/ 0 // y is equal to 0
Precedence and Associativity
Precedenceは優先度、Associativityは結合、ですね。
まぁ、要は算数のお話です。後で説明する演算子のオーバーロードの時に関わってきます。
2 + 3 * 4 % 5 // this equals 4 // 実際には以下の順番で実行される 2 + ((3 * 4) % 5) 2 + (12 % 5) 2 + 2
Operator Functions
先に言っておきますが、代入演算子(=)と三項演算子(a ? b : c)はオーバーロードできません。悪しからず。
それじゃあまずは中置演算子です。
struct Vector2D { var x = 0.0, y = 0.0 } // 中置演算子の場合は@infixと言うAttributeを使用する @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)
次は前置演算子です。
// 前置演算子の場合は@prefix // 後置演算子であれば@postfixを使用する @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)
複合代入演算子の場合はassignmentと言うAttributeを使用します。
@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)
prefixやpostfixと併用することでインクリメントやデクリメントを表現することもできます。
@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)
@infix func == (left: Vector2D, right: Vector2D) -> Bool { return (left.x == right.x) && (left.y == right.y) } @infix func != (left: Vector2D, right: Vector2D) -> Bool { // 事前にVector2Dの「==」をオーバーロードしているのでこう書ける 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."
Custom Operators
Swiftで使用できる演算子を組み合わせて自分だけの最強の演算子を作ることもできます。
operator prefix +++ {} @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)
また、自作の演算子に関しても優先度と結合規則を設定することができます。
operator infix +- { // precedenceに設定できる値は0〜255(デフォルトは100) precedence 140 // associativityに設定できるのはleft, right, none(デフォルトはnone) associativity left } 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)
このコード例だとちょっと説明が足りてないので、Wikipediaに書いてあるやつも引用してみます。
// 中置演算子 ** を定義する operator infix ** { precedence 155 associativity right } // ** 演算子を Double型のべき乗として実装する @infix func ** (lhs: Double, rhs: Double) -> Double { return pow(lhs, rhs) } 2 ** 3 // 8.0(2の3乗) 0.5 * 2 ** 3 ** 2 // 256.0 // **演算子は *演算子よりも優先順位が高く、右結合として定義したので // この式は 0.5 * (2 ** (3 ** 2)) の意味となる
とまぁ、書いてある通りです。優先度に関してはleftならleft同士で、rightならright同士で比較するみたいです。
そこまで面倒なことを考えるならメソッドなり関数なりを作ってしまった方がいいんじゃないかって気もしますが。