This commit is contained in:
949478479
2015-12-01 17:02:31 +08:00
parent 0e459f395c
commit d62ffd03f6

View File

@ -19,13 +19,13 @@
- [运算符函数](#operator_functions)
- [自定义运算符](#custom_operators)
除了在之前介绍过的[基本运算符](./02_Basic_Operators.html)Swift 中还有许多可以对数值进行复杂操作的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
除了在之前介绍过的[基本运算符](./02_Basic_Operators.html)Swift 中还有许多可以对数值进行复杂运算的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
与 C 语言中的算术运算符不同Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。
自定义结构体、类和枚举时,如果也为它们提供标准 Swift 运算符的实现,将会非常有用。在 Swift 中自定义运算符非常简单,运算符也会针对不同类型使用对应实现。
我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中、前、后和赋值运算符,以及相应的优先级与结合性。这些运算符在代码中可以像预定义的运算符一样使用,我们甚至可以扩展已有的类型以支持自定义的运算符。
我们不用被预定义的运算符所限制。在 Swift 中可以自由地定义中、前、后和赋值运算符,以及相应的优先级与结合性。这些运算符在代码中可以像预定义的运算符一样使用,我们甚至可以扩展已有的类型以支持自定义的运算符。
<a name="bitwise_operators"></a>
## 位运算符
@ -42,9 +42,9 @@ Swift 支持 C 语言中的全部位运算符,接下来会一一介绍。
![Art/bitwiseNOT_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseNOT_2x.png)
按位取反运算符是一个前运算符,需要直接放在操作数的之前,并且它们之间不能添加任何空格:
按位取反运算符是一个前运算符,需要直接放在运算的数之前,并且它们之间不能添加任何空格:
```
```swift
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
```
@ -56,13 +56,13 @@ let invertedBits = ~initialBits // 等于 0b11110000
<a name="bitwise_and_operator"></a>
### 按位与运算符
按位与运算符(`&`)可以对两个数的比特位进行合并。它返回一个新的数,只有当两个操作数的对应位都为 `1` 的时候,新数的对应位才为 `1`
按位与运算符(`&`)可以对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位都为 `1` 的时候,新数的对应位才为 `1`
![Art/bitwiseAND_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseAND_2x.png)
在下面的示例当中,`firstSixBits``lastSixBits` 中间 4 个位的值都为 `1`。按位与运算符对它们进行了运算,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`
```
```swift
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
@ -71,13 +71,13 @@ let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
<a name="bitwise_or_operator"></a>
### 按位或运算符
按位或运算符(`|`)可以对两个数的比特位进行比较。它返回一个新的数,只要两个操作数的对应位中有任意一个为 `1` 时,新数的对应位就为 `1`
按位或运算符(`|`)可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有任意一个为 `1` 时,新数的对应位就为 `1`
![Art/bitwiseOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseOR_2x.png "Art/bitwiseOR_2x.png")
在下面的示例中,`someBits``moreBits` 不同的位会被设置为 `1`。接位或运算符对它们进行了运算,得到二进制数值 `11111110`,等价于无符号十进制数的 `254`
```
```swift
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110
@ -86,13 +86,13 @@ let combinedbits = someBits | moreBits // 等于 11111110
<a name="bitwise_xor_operator"></a>
### 按位异或运算符
按位异或运算符(`^`)可以对两个数的比特位进行比较。它返回一个新的数,当两个操作数的对应位不相同时,新数的对应位就为 `1`
按位异或运算符(`^`)可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 `1`
![Art/bitwiseXOR_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitwiseXOR_2x.png "Art/bitwiseXOR_2x.png")
在下面的示例当中,`firstBits``otherBits` 都有一个自己的位为 `1` 而对方的对应位为 `0` 的位。 按位异或运算符将新数的这两个位都设置为 `1`,同时将其它位都设置为 `0`
```
```swift
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
@ -106,7 +106,7 @@ let outputBits = firstBits ^ otherBits // 等于 00010001
对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2同样地将一个整数右移一位等价于将这个数除以 2。
<a name="shifting_behavior_for_unsigned_integers"></a>
#### 无符号整数的移位操作
#### 无符号整数的移位运算
对无符号整数进行移位的规则如下:
@ -120,9 +120,9 @@ let outputBits = firstBits ^ otherBits // 等于 00010001
![Art/bitshiftUnsigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftUnsigned_2x.png "Art/bitshiftUnsigned_2x.png")
下面的代码演示了 Swift 中的移位操作
下面的代码演示了 Swift 中的移位运算
```
```swift
let shiftBits: UInt8 = 4 // 即二进制的 00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
@ -131,9 +131,9 @@ shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
```
可以使用移位操作对其他的数据类型进行编码和解码:
可以使用移位运算对其他的数据类型进行编码和解码:
```
```swift
let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC即 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66 即 102
@ -151,9 +151,9 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
最后,蓝色部分通过对 `0xCC6699``0x0000FF` 进行按位与运算得到 `0x000099`。这里不需要再向右移位,所以结果为 `0x99` ,也就是十进制数值的 `153`
<a name="shifting_behavior_for_signed_integers"></a>
#### 有符号整数的移位操作
#### 有符号整数的移位运算
对比无符号整数,有符号整数的移位操作相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特位的有符号整数的,但是其中的原理对任何位数的有符号整数都是通用的。)
对比无符号整数,有符号整数的移位运算相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 比特位的有符号整数的,但是其中的原理对任何位数的有符号整数都是通用的。)
有符号整数使用第 1 个比特位(通常被称为符号位)来表示这个数的正负。符号位为 `0` 代表正数,为 `1` 代表负数。
@ -175,38 +175,37 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
负数的表示通常被称为二进制补码表示。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。
首先,如果想对 `-1``-4` 进行加法操作,我们只需要将这两个数的全部 8 个比特位进行相加,并且将计算结果中超出 8 位的数值丢弃:
首先,如果想对 `-1``-4` 进行加法运算,我们只需要将这两个数的全部 8 个比特位进行相加,并且将计算结果中超出 8 位的数值丢弃:
![Art/bitshiftSignedAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSignedAddition_2x.png "Art/bitshiftSignedAddition_2x.png")
其次,使用二进制补码可以使负数的按位左移和右移操作得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的对有符号整数的右移有一个额外的规则
其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的对有符号整数的右移有一个额外的规则
* 当对正整数进行按位右移操作时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 `0`
* 当对正整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 `0`
![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png")
这个行为可以确保有符号整数的符号位不会因为右移操作而改变,这通常被称为算术移位。
这个行为可以确保有符号整数的符号位不会因为右移运算而改变,这通常被称为算术移位。
由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 `0`。在移位的过程中保持符号位不变,意味着负整数在接近 `0` 的过程中会一直保持为负。
<a name="overflow_operators"></a>
## 溢出运算符
在默认情况下当向一个整数赋超过它容量的值时Swift 默认会报错,而不是生成一个无效的数。这个行为我们操作过大或着过小的数的时候提供了额外的安全性。
在默认情况下,当向一个整数赋超过它容量的值时Swift 默认会报错,而不是生成一个无效的数。这个行为我们在运算过大或着过小的数的时候提供了额外的安全性。
例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768``32767`,当为一个 `Int16` 型变量赋的值超过这个范围时,系统就会报错:
```
```swift
var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767, 这是 Int16 能容纳的最大整数
// potentialOverflow 的值是 32767这是 Int16 能容纳的最大整数
potentialOverflow += 1
// 这里会报错
```
为过大或者过小的数值提供错误处理,能让我们在处理边界值时更加灵活。
然而,也可以选择让系统在数值溢出的时候采取截断操作,而非报错。可以使用 Swift 提供的三个溢出操作符(`overflow operators`)来让系统支持整数溢出运算。这些操作符都是以 `&` 开头的:
然而,也可以选择让系统在数值溢出的时候采取截断处理,而非报错。可以使用 Swift 提供的三个溢出运算符来让系统支持整数溢出运算。这些运算符都是以 `&` 开头的:
* 溢出加法 `&+`
* 溢出减法 `&-`
@ -217,44 +216,42 @@ potentialOverflow += 1
数值有可能出现上溢或者下溢。
这个示例演示了当我们对一个无符号整数使用溢出加法(`&+`)进行上溢运算时会发生什么:
```
这个示例演示了当我们对一个无符号整数使用溢出加法`&+`进行上溢运算时会发生什么:
```swift
var unsignedOverflow = UInt8.max
// unsignedOverflow 等于 UInt8 所能容纳的最大整数 255
unsignedOverflow = unsignedOverflow &+ 1
// 此时 unsignedOverflow 等于 0
```
`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用了溢出加法运算符(`&+`)对其进行加 1 操作。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 0
`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数`255`,以二进制表示即 `11111111`。然后使用了溢出加法运算符`&+`对其进行加 `1` 运算。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 `0`
![Art/overflowAddition_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowAddition_2x.png "Art/overflowAddition_2x.png")
同样地,当我们对一个无符号整数使用溢出减法(`&-`)进行下溢运算时也会产生类似的现象:
同样地,当我们对一个无符号整数使用溢出减法`&-`进行下溢运算时也会产生类似的现象:
```
```swift
var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0
unsignedOverflow = unsignedOverflow &- 1
// 此时 unsignedOverflow 等于 255
```
`UInt8` 型整数能容纳的最小值是 0,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 1 操作时,数值会产生下溢并被截断为 `11111111` 也就是十进制数值的 255。
`UInt8` 型整数能容纳的最小值是 `0`,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 `1` 运算时,数值会产生下溢并被截断为 `11111111` 也就是十进制数值的 `255`
![Art/overflowUnsignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowUnsignedSubtraction_2x.png "Art/overflowAddition_2x.png")
溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算,正如[按位左移/右移运算符](#bitwise_left_and_right_shift_operators)所描述的。
溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算,正如[按位左移右移运算符](#bitwise_left_and_right_shift_operators)所描述的。
```
```swift
var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容纳的最小整数 -128
signedOverflow = signedOverflow &- 1
// 此时 signedOverflow 等于 127
```
`Int8` 型整数能容纳的最小值是 -128以二进制表示即 `10000000`。当使用溢出减法操作符对其进行减 1 操作时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整数所能容纳的最大值。
`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整数所能容纳的最大值。
![Art/overflowSignedSubtraction_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/overflowSignedSubtraction_2x.png "Art/overflowSignedSubtraction_2x.png")
@ -263,81 +260,79 @@ signedOverflow = signedOverflow &- 1
<a name="precedence_and_associativity"></a>
## 优先级和结合性
运算符的优先级(`precedence`)使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
结合性(`associativity`)定义了相同优先级的运算符是如何结合(或关联)的 —— 是与左边结合为一组,还是与右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”。
结合性定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”。
在复合表达式的运算顺序中,运算符的优先级和结合性是非常重要的。举例来说,为什么下面这个表达式的运算结果是 `4`
在复合表达式的运算顺序中,运算符的优先级和结合性是非常重要的。举例来说,运算符优先级解释了为什么下面这个表达式的运算结果`17`
```swift
2 + 3 * 4 % 5
// 结果是 4
2 + 3 % 4 * 5
// 结果是 17
```
如果严格地从左到右进行运算,则运算的过程是这样的:
如果完全从左到右进行运算,则运算的过程是这样的:
- 2 + 3 = 5
- 5 * 4 = 20
- 20 % 5 = 0
- 5 % 4 = 1
- 1 * 5 = 5
但是正确答案是 `4` 而不是 `0`。优先级高的运算符要先于优先级低的运算符进行计算。与C语言类似,在 Swift 中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。
但是正确答案是 `17` 而不是 `5`。优先级高的运算符要先于优先级低的运算符进行计算。与 C 语言类似,在 Swift 中,乘法运算符`*`与取余运算符`%`的优先级高于加法运算符`+`。因此,它们的计算顺序要先于加法运算。
而乘法与取余的优先级相同。这时为了得到正确的运算顺序,还需要考虑结合性。乘法与取余运算都是左结合的。可以将这考虑成为这两部分表达式都隐式地加上了括号:
```swift
2 + ((3 * 4) % 5)
2 + ((3 % 4) * 5)
```
`(3 * 4) = 12`,所以表达式相当于:
`(3 % 4)` 等于 `3`,所以表达式相当于:
```swift
2 + (12 % 5)
2 + (3 * 5)
```
`12 % 5 = 2`,所以表达式相当于:
`3 * 5` 等于 `15`,所以表达式相当于:
```swift
2 + 2
2 + 15
```
时可以容易地看出计算结果为 `4`
此计算结果为 `17`
如果想查看完整的 Swift 运算符优先级和结合性规则,请参考[表达式](../chapter3/04_Expressions.html)。如果想查看 Swift 标准库提供所有的操作符,请查看[Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)
如果想查看完整的 Swift 运算符优先级和结合性规则,请参考[表达式](../chapter3/04_Expressions.html)。如果想查看 Swift 标准库提供所有的运算符,请查看 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)
> 注意
> 对于C语言和 Objective-C 来说Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们于那些基于C的语言不是完全一致的。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然是按照你所想的那样去执行
> 注意
> 相对 C 语言和 Objective-C 来说Swift 的运算符优先级和结合性规则更加简洁和可预测。但是,这也意味着它们相较于 C 语言及其衍生语言并不是完全一致的。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然符合你的预期
<a name="operator_functions"></a>
## 运算符函数
类和结构可以为现有的操作符提供自定义的实现,这通常被称为运算符重载(`overloading`)
类和结构可以为现有的运算符提供自定义的实现,这通常被称为运算符重载。
下面的例子展示了如何为自定义的结构实现加法操作符(`+`)。算术加法运算符是一个目运算符(`binary operator`),因为它可以对两个目标进行操作,同时它还是中缀(`infix`)运算符,因为它出现在两个目标中间。
下面的例子展示了如何为自定义的结构实现加法运算符(`+`。算术加法运算符是一个目运算符,因为它可以对两个值进行运算,同时它还是中缀运算符,因为它出现在两个中间。
例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量`(x, y)`,紧接着定义了一个可以对两个 `Vector2D` 结构体进行相加的运算符函数(`operator function`)
例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量 `(x, y)`,紧接着定义了一个可以对两个 `Vector2D` 结构体进行相加的运算符函数:
```swift
struct Vector2D {
var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
```
该运算符函数被定义为一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D`输入参数,同时有一个 `Vector2D` 类型的返回值。
该运算符函数被定义为一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。
在这个实现中,输入参数分别被命名为 `left``right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 对象。函数返回了一个新的 `Vector2D` 的对象,这个对象`x``y` 分别等于两个参数对象`x``y` 的值之和。
在这个实现中,输入参数分别被命名为 `left``right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例`x``y` 分别等于作为参数的两个实例`x``y` 的值之和。
这个函数被定义成全局的,而不是 `Vector2D` 结构的成员方法,所以任意两个 `Vector2D` 对象都可以使用这个中缀运算符:
这个函数被定义成全局的,而不是 `Vector2D` 结构的成员方法,所以任意两个 `Vector2D` 实例都可以使用这个中缀运算符:
```swift
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0)
// combinedVector 是一个新的 Vector2D 实例,值为 (5.0, 5.0)
```
这个例子实现两个向量 `(3.01.0)``(2.04.0)` 的相加,并得到新的向量 `(5.05.0)`。这个过程如下图示:
@ -347,9 +342,9 @@ let combinedVector = vector + anotherVector
<a name="prefix_and_postfix_operators"></a>
### 前缀和后缀运算符
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符(`unary operators`)的实现。单目运算符只有一个操作目标。当运算符出现在操作目标之前时,它就是前缀(`prefix`)的(比`-a`),而当它出现在操作目标之后时,它就是后缀(`postfix`)的(比`i++`)
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在之前时,它就是前缀的(例`-a`,而当它出现在之后时,它就是后缀的(例`i++`
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 限定符:
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符:
```swift
prefix func - (vector: Vector2D) -> Vector2D {
@ -357,22 +352,21 @@ prefix func - (vector: Vector2D) -> Vector2D {
}
```
这段代码为 `Vector2D` 类型实现了单目运算符(`-a`)。由于单目减运算符是前缀运算符,所以这个函数需要加上 `prefix` 限定符。
这段代码为 `Vector2D` 类型实现了单目负号运算符。由于该运算符是前缀运算符,所以这个函数需要加上 `prefix` 修饰符。
对于简单数值,单目运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,单目减运算将其 `x``y` 属性的正负性都进行了改变
对于简单数值,单目负号运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,运算将其 `x``y` 属性的正负性都进行了改变
```swift
let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例
let alsoPositive = -negative
// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例
```
<a name="compound_assignment_operators"></a>
### 复合赋值运算符
复合赋值运算符(`Compound assignment operators`)将赋值运算符(`=`)与其它运算符进行结合。如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。
复合赋值运算符将赋值运算符`=`与其它运算符进行结合。如,将加法与赋值结合成加法赋值运算符`+=`。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。
```swift
func += (inout left: Vector2D, right: Vector2D) {
@ -389,7 +383,7 @@ original += vectorToAdd
// original 的值现在为 (4.0, 6.0)
```
还可以将赋值与 `prefix``postfix` 限定符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符(`++a`)
还可以将赋值与 `prefix``postfix` 修饰符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符:
```swift
prefix func ++ (inout vector: Vector2D) -> Vector2D {
@ -398,7 +392,7 @@ prefix func ++ (inout vector: Vector2D) -> Vector2D {
}
```
这个前缀自增运算符使用了前面定义的加法赋值操作。它对 `Vector2D``x``y` 属性都进行了加 `1` 操作,再将结果返回:
这个前缀自增运算符使用了前面定义的加法赋值运算。它对 `Vector2D``x``y` 属性都进行了加 `1` 运算,再将结果返回:
```swift
var toIncrement = Vector2D(x: 3.0, y: 4.0)
@ -407,15 +401,15 @@ let afterIncrement = ++toIncrement
// afterIncrement 的值同样为 (4.0, 5.0)
```
> 注意
> 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值符可以被重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。
> 注意
> 不能对默认的赋值运算符`=`进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。
<a name="equivalence_operators"></a>
### 等价操作
### 等价运算
自定义的类和结构体没有对等价操作符(`equivalence operators`)进行默认实现,等价操作符通常被称为“相等”操作符(`==`)与“不等”操作符(`!=`)。对于自定义类型Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
自定义的类和结构体没有对等价运算符进行默认实现,等价运算符通常被称为“相等”运算符(`==`与“不等”运算符(`!=`。对于自定义类型Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
为了使用等价操作符来对自定义的类型进行判等操作,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
```swift
func == (left: Vector2D, right: Vector2D) -> Bool {
@ -426,9 +420,9 @@ func != (left: Vector2D, right: Vector2D) -> Bool {
}
```
上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 对象是否相等。对于 `Vector2D` 类型来说,“相等”意味“两个实例的 `x`属性 `y` 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”操作符(`!=`),它简单地将“相等”操作符进行取反后返回。
上述代码实现了“相等”运算符`==`来判断两个 `Vector2D` 实例是否相等。对于 `Vector2D` 类型来说,“相等”意味“两个实例的 `x` 属性和 `y` 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”运算符(`!=`,它简单地将“相等”运算符的结果进行取反后返回。
现在我们可以使用这两个运算符来判断两个 `Vector2D` 对象是否相等
现在我们可以使用这两个运算符来判断两个 `Vector2D` 实例是否相等
```swift
let twoThree = Vector2D(x: 2.0, y: 3.0)
@ -436,21 +430,21 @@ let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
print("These two vectors are equivalent.")
}
// prints "These two vectors are equivalent."
// 打印 “These two vectors are equivalent.
```
<a name="custom_operators"></a>
## 自定义运算符
除了实现标准运算符,在 Swift 中还可以声明和实现自定义运算符(`custom operators`)。可以用来自定义运算符的字符列表请参考[操作](../chapter3/02_Lexical_Structure.html#operators)
除了实现标准运算符,在 Swift 中还可以声明和实现自定义运算符。可以用来自定义运算符的字符列表请参考[运算](../chapter3/02_Lexical_Structure.html#operators)
新的运算符要在全局作用域内,使用 `operator` 关键字进行声明,同时还要指定 `prefix``infix` 或者 `postfix` 限定符:
新的运算符要使用 `operator` 关键字在全局作用域内进行定义,同时还要指定 `prefix``infix` 或者 `postfix` 修饰符:
```swift
prefix operator +++ {}
```
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法操作符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y`属性的值翻倍:
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍:
```swift
prefix func +++ (inout vector: Vector2D) -> Vector2D {
@ -459,7 +453,7 @@ prefix func +++ (inout vector: Vector2D) -> Vector2D {
}
```
`Vector2D``+++` 的实现和 `++` 的实现很相似, 唯一不同的是前者对自身进行相加, 而后者是与另一个值为 `(1.0, 1.0)` 的向量相加.
`Vector2D``+++` 的实现和 `++` 的实现很相似唯一不同的是前者对自身进行相加而后者是与另一个值为 `(1.0, 1.0)` 的向量相加
```swift
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
@ -467,16 +461,17 @@ let afterDoubling = +++toBeDoubled
// toBeDoubled 现在的值为 (2.0, 8.0)
// afterDoubling 现在的值也为 (2.0, 8.0)
```
<a name="precedence_and_associativity_for_custom_infix_operators"></a>
### 自定义中缀运算符的优先级和结合性
自定义的中缀(`infix`)运算符也可以指定优先级(`precedence`)和结合性(`associativity`)。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
自定义的中缀运算符也可以指定优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
结合性(`associativity`)可取的值有` left``right``none`。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的操作数进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的操作数进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。
结合性可取的值有` left``right``none`。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。
结合性(`associativity`)的默认值是 `none`,优先级(`precedence`)如果没有指定,则默认 `100`
结合性的默认值是 `none`,优先级的默认 `100`
以下例子定义了一个新的中缀运算符 `+-`,此操作符是左结合的,并且它的优先级为 `140`
以下例子定义了一个新的中缀运算符 `+-`,此运算符的结合性为 `left`,并且它的优先级为 `140`
```swift
infix operator +- { associativity left precedence 140 }
@ -486,10 +481,10 @@ func +- (left: Vector2D, right: Vector2D) -> Vector2D {
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一个 Vector2D 类型,并且它的值为 (4.0, -2.0)
// plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0)
```
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“加型”运算符,所以将它的结合性和优先级被设置为(`left``140`),这与 `+``-` 等默认的中缀加型操作符是相同的。完整的 Swift 操作符默认结合性与优先级请参考[Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“加型”运算符,所以将它的结合性和优先级被分别设置为 `left``140`,这与 `+``-` 等默认的中缀“相加型”运算符是相同的。关于 Swift 标准库提供的运算符的结合性与优先级请参考 [Swift Standard Library Operators Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
> 注意
> 当定义前缀与后缀操作符的时候,我们并没有指定优先级。然而,如果对同一个操作数同时使用前缀与后缀操作符,则后缀操作符会先被执行
> 注意
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个同时使用前缀与后缀运算符,则后缀运算符会先参与运算