Swift中的运算符重载
一、概念
类和结构体可以为现有的运算符提供自定义的实现,称为运算符重载。
二、种类
- 一元运算符重载
- 组合赋值运算符重载
- 等价运算符重载
- 自定义运算符重载
the code
struct Vector2D {
var x = 0.0
var y = 0.0
}
let vector2D = Vector2D(x: 2.0, y: 3.0)
let anotherVector2D = Vector2D(x: 1.0, y: 4.0)
//正常不使用运算符重载也可以实现,但是这样写不通用也不方便后期修改
let newVector2D = Vector2D(x: vector2D.x + anotherVector2D.x, y: vector2D.y + anotherVector2D.y)
print(newVector2D)
如果使用运算符重载
struct Vector2D {
var x = 0.0
var y = 0.0
}
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
let vector2D = Vector2D(x: 2.0, y: 3.0)
let anotherVector2D = Vector2D(x: 1.0, y: 4.0)
//正常不使用运算符重载也可以实现,但是这样写不通用也不方便后期修改
//let newVector2D = Vector2D(x: vector2D.x + anotherVector2D.x, y: vector2D.y + anotherVector2D.y)
//如果使用运算符重载,直接将两个变量进行相加就可以了
let newVector2D = vector2D + anotherVector2D
print(newVector2D)
一元运算符重载:要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 func 关键字之前指定 prefix 或者 postfix 限定符。
struct Vector2D {
var x = 0.0
var y = 0.0
}
//一元运算符重载: 要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 func 关键字之前指定 prefix 或者 postfix 限定符
extension Vector2D {
//使用前缀运算符
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
//使用后缀运算符
extension Vector2D {
static postfix func ++ (vector: inout Vector2D) -> Vector2D {
vector = vector + Vector2D(x: 1, y: 1)
return vector
}
}
}
let vector = Vector2D(x: 2.0, y: 2.0)
let newVector = -vector
print(newVector)
组合赋值运算符重载:
- 组合赋值运算符将赋值运算符( = )与其它运算符进行结合
- 在实现的时候,需要把运算符的左参数设置成 inout 类型,因为这个参数的值会在运算 符函数内直接被修改
struct Vector2D {
var x = 0.0
var y = 0.0
}
extension Vector2D {
static func += (left: inout Vector2D, right: inout Vector2D) {
left = left + right
}
}
var vector1 = Vector2D(x: 5, y: 8)
var vector2 = Vector2D(x: 2, y: 9)
vector1 += vector2
print("vector1 x is \(vector1.x), y is \(vector1.y)")
等价运算符重载
- 自定义类和结构体不接收等价运算符的默认实现,也就是所谓的“等于”运算符( == ) 和“不等于”运算符( != )
- 要使用等价运算符来检查你自己类型的等价,需要和其他中缀运算符一样提供一个“等 于”运算符,并且遵循标准库的 Equatable 协议
struct Vector2D {
var x = 0.0, y = 0.0
}
//要扩展的类遵循了标准库的 Equatable 协议
extension Vector2D: Equatable {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return left.x == right.x && left.y == right.y
}
}
var vector1 = Vector2D(x: 2, y: 4)
var vector2 = Vector2D(x: 1, y: 4)
print(vector1 == vector2)
在Swift中会为以下自定义类型提供默认Equatable协议(等价运算符合成)的实现
- 没有关联类型的枚举
- 只拥有遵循 Equatable 协议关联类型的枚举
- 只拥有遵循 Equatable 协议存储属性的结构体
//在声明结构体的时候声明的是存储型属性,并且遵循 Equatable 协议
struct Vector2D: Equatable {
var x = 0.0
var y = 0.0
}
let vector1 = Vector2D(x: 2.0, y: 3.0)
let vector2 = Vector2D(x: 2.0, y: 4.0)
print(vector1 == vector2)
自定义运算符
- 新的运算符要在全局作用域内,使用 operator 关键字进行声明,同时还要指定 prefix 、infix 或者 postfix 限定符
//自定义运算符 prefix:前缀 infix:中缀 postfix:后缀
struct Vector2D {
var x = 0.0, y = 0.0
}
// 使用了 operator 关键字进行声明,并且指定了 prefix 前缀限定符
prefix operator +++
extension Vector2D {
static prefix func +++(vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
var vector1 = Vector2D(x: 2.0, y: 4.0)
let newVector = +++vector1
print("newVector x is \(newVector.x), y is \(newVector.y)")
自定义中缀运算符的优先级和结合性
- 自定义的中缀( infix )运算符也可以指定优先级和结合性
- 每一个自定义的中缀运算符都属于一个优先级组
- 优先级组指定了自定义中缀运算符和其他中缀运算符的关系
/*
precedencegroup
定义了一个操作符优先级别。操作符优先级的定义和类型声明有些相似,一个操作符比需 要属于某个特定的优先级。Swift 标准库中已经定义了一些常用的运算优先级组,比如加法 优先级 ( AdditionPrecedence ) 和乘法优先级 ( MultiplicationPrecedence ) 等,你可以在这里找 到完整的列表。如果没有适合你的运算符的优先级组,你就需要像我们在例子中做得这 样,自己指定结合律方式和优先级顺序了。
associativity
定义了结合律,即如果多个同类的操作符顺序出现的计算顺序。比如常⻅的加法和减法都 是 left ,就是说多个加法同时出现时按照从左往右的顺序计算 (因为加法满足交换律,所 以这个顺序无所谓,但是减法的话计算顺序就很重要了)。点乘的结果是一个 Double ,不 再会和其他点乘结合使用,所以这里是 none ;
higherThan
运算的优先级,点积运算是优先于乘法运算的。除了 higherThan ,也支持使用 lowerThan 来指定优先级低于某个其他组。
infix
表示要定义的是一个中位操作符,即前后都是输入;其他的修饰子还包括 prefix 和 postfix ,不再赘述;
*/
struct Vector2D {
var x = 0.0, y = 0.0
}
//声明自定义运算符: 加减
infix operator +- : PlusMinusPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
//声明自定义运算符: 乘平方和
infix operator *^ : MultiplicationPrecedence
extension Vector2D {
static func *^ (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x * right.x, y: left.y * left.y + right.y * right.y)
}
}
//自定义中缀运算符的优先级和结合性
precedencegroup PlusMinusPrecedence {
associativity: none // 结合性(left、right、none)
higherThan: AdditionPrecedence // 比谁的优先级高
lowerThan: MultiplicationPrecedence // 比谁的优先级低
assignment: true // 在可选链操作中拥有跟赋值运算符一样的优先级
}
var vector1 = Vector2D(x: 2.0, y: 6.0)
var vector2 = Vector2D(x: 3.0, y: 4.0)
var vector3 = Vector2D(x: 2.0, y: 4.0)
let newVector = vector1 +- vector2 *^ vector3
print("newVector x is \(newVector.x), y is \(newVector.y)")