// 语法最后一章了老铁~~~
// 个人认为: 高级运算符也是容易出错的地方
//“Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(&+)。所有的这些溢出运算符都是以 & 开头的。”
//1. 位运算符
//“位运算符可以操作数据结构中每个独立的 比特位 。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。”
//“Swift 支持 C 语言中的全部位运算符”
//1.1 “按位取反运算符”
//“按位取反运算符(~)可以对一个数值的全部比特位进行取反,“按位取反运算符是一个前缀运算符,需要直接放在运算的数之前,并且它们之间不能添加任何空格”
//1.2 按位与运算符
//“按位与运算符(&)可以对两个数的比特位进行合并。它返回一个新的数,只有当两个数的对应位都为 1 的时候,新数的对应位才为 1”
//1.3“按位或运算符”
//“按位或运算符(|)可以对两个数的比特位进行比较。它返回一个新的数,只要两个数的对应位中有任意一个为 1 时,新数的对应位就为 1:”
//1.4 按位异或运算符
//“按位异或运算符(^)可以对两个数的比特位进行比较。它返回一个新的数,当两个数的对应位不相同时,新数的对应位就为 1:”
//1.5 “按位左移、右移运算符”
//“按位左移运算符(<<)和按位右移运算符(>>)可以对一个数的所有位进行指定位数的左移和右移”
//“将一个整数左移一位,等价于将这个数乘以 2,同样地,将一个整数右移一位,等价于将这个数除以 2。”
//2. 溢出运算符
//“在默认情况下,当向一个整数赋予超过它容量的值时,Swift 默认会报错,而不是生成一个无效的数。这个行为为我们在运算过大或着过小的数的时候提供了额外的安全性”
//“也可以选择让系统在数值溢出的时候采取截断处理,而非报错。可以使用 Swift 提供的三个溢出运算符来让系统支持整数溢出运算。这些运算符都是以 & 开头的:
/*
溢出加法 &+
溢出减法 &-
溢出乘法 &*”
*/
//2.1 数值溢出
var unsignedOverFlow = UInt8.max
unsignedOverFlow = unsignedOverFlow &+ 1
//此时unsignedOverFlow 等于0
var unsignedOverFlowMin = UInt8.min
//“// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0”
unsignedOverFlowMin = unsignedOverFlowMin &- 1
//此时unsignedOverFlowMin 等于255
//2.2“溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算”
//“var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容纳的最小整数 -128
//signedOverflow = signedOverflow &- 1
// 此时 signedOverflow 等于 127”
//3. 优先级与结合性
//“运算符的优先级使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。”
//“结合性定义了相同优先级的运算符是如何结合的,也就是说,是与左边结合为一组,还是与右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的。
//4.运算符函数
// “类和结构体可以为现有的运算符提供自定义的实现,这通常被称为运算符重载。”
struct Vector2D {
var x = 0.0, y = 0.0
}
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
//“例子中定义了一个名为 Vector2D 的结构体用来表示二维坐标向量 (x, y),紧接着定义了一个可以对两个 Vector2D 结构体进行相加的运算符函数”
//“该运算符函数被定义为 Vector2D 上的一个类方法,并且函数的名字与它要进行重载的 + 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 Vector2D 的一个扩展中,而不是 Vector2D 结构体声明内。”
//4.1前缀和后缀运算符
//“要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 func 关键字之前指定 prefix 或者 postfix 修饰符:”
extension Vector2D {
static prefix func -(vector:Vector2D)->Vector2D{
return Vector2D(x:-vector.x,y:-vector.y)
}
}
//“这段代码为 Vector2D 类型实现了单目负号运算符。由于该运算符是前缀运算符,所以这个函数需要加上 prefix 修饰符。”
//“对于简单数值,单目负号运算符可以对它们的正负性进行改变。对于 Vector2D 来说,该运算将其 x 和 y 属性的正负性都进行了改变”
let positive = Vector2D(x:3.0,y:4.0)
let negative = -positive
//“ negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例”
//4.2 复合赋值运算符
//“复合赋值运算符将赋值运算符(=)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(+=)。在实现的时候,需要把运算符的左参数设置成 inout 类型,因为这个参数的值会在运算符函数内直接被修改。”
//“不能对默认的赋值运算符(=)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 (a ? b : c) 进行重载。”
//4.3 等价运算符
// “自定义的类和结构体没有对等价运算符进行默认实现,等价运算符通常被称为“相等”运算符(==)与“不等”运算符(!=)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。”
//“为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:”
extension Vector2D {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
static func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
}
//“上述代码实现了“相等”运算符(==)来判断两个 Vector2D 实例是否相等。对于 Vector2D 类型来说,“相等”意味着“两个实例的 x 属性和 y 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”运算符(!=),它简单地将“相等”运算符的结果进行取反后返回”
let twoThree = Vector2D(x:2.0,y:3.0)
let anotherTwoThree = Vector2D(x:2.0,y:3.0)
if twoThree == anotherTwoThree {
print("these two vectors are equivalent")
}
//5. 自定义运算符
//“在 Swift 中还可以声明和实现自定义运算符”
//“新的运算符要使用 operator 关键字在全局作用域内进行定义,同时还要指定 prefix、infix 或者 postfix 修饰符”
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled 现在的值为 (2.0, 8.0)
// afterDoubling 现在的值也为 (2.0, 8.0)”