Swift 自定义运算符
1. 运算符
Swift 提供了一组对于 C 或者 Objective-C 开发者来说十分熟悉的运算符,并且补充了一些新的(特别需要注意的是,区间运算符和空值合并(nil coalescing)运算符):
如下面这些运算符
符号 | - |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 余 |
+=: | 赋值增 |
-=: | 赋值减 |
*=: | 赋值乘 |
/= | 赋值除 |
! | 非 |
~ | 按位取反 |
…… | …… |
2. 重载
Swfit 是可以重载运算符的,其能够让现有的运算符像 +
在其它的类型中起作用。
为了重载一个运算符,需要为运算符号简单的定义一个新的函数,并且要有适当的参数个数。
例如,重载 * 来让一个字符串重复某个特定的次数:
func * (left: String, right: Int) -> String {
if right <= 0 {
return ""
}
var result = left
for _ in 1..<right {
result += left
}
return result
}
print("a" * 6)//aaaaaa
然而,这是一个有争议的语言特性。
如下语句:
[1, 2] + [3, 4] // [1, 2, 3, 4]
两个数组相加,想得到 [1, 2, 3, 4]
,但重载以后:
func +(left: [Double], right: [Double]) -> [Double] {
var sum = [Double](repeating: 0.0, count: left.count)
for (i, _) in left.enumerated() {
sum[i] = left[i] + right[i]
}
return sum
}
得到的是 [4.0, 6.0]
运算符重载的问题会造成:语意不清。在使用时,要注意具体应用,并避免重载。
- 计算整数的和: 1 + 2 // 3
- 计算浮点数的和: 1.0 + 2.0 // 3.0
- 字符串连接: "a" + "b" // "ab"
- 数组连接: ["foo"] + ["bar"] // ["foo", "bar"]
如果需要重载运算符要考虑多种情况,避免出现不可预见的bug.
3. 自定义运算符
自定义运算符,一般可以分三步
-
1.定义优先级组
如果需要自己设置优先级组,可以自己定义,使用
precedencegroup
关键字声明,如果不需要,可以省去。precedencegroup
定义了一个操作符优先级别。操作符优先级的定义和类型声明有些相似,一个操作符比需要属于某个特定的优先级。Swift 标准库中已经定义了一些常用的运算优先级组,比如加法优先级 (AdditionPrecedence) 和乘法优先级 (MultiplicationPrecedence) 等,你也可以查看完整的列表。如果没有适合你的运算符的优先级组,你就需要像在例子中做的这样,自己指定方式和优先级顺序。precedencegroup CustomerPrecedence { /// 优先从左向右, left, right or none associativity: left higherThan: MultiplicationPrecedence//优先级,比乘法运算高 // lowerThan: AdditionPrecedence // 优先级, 比加法运算低 assignment: false // 是否是赋值运算 }
associativity
定义了结合律,即如果多个同类的操作符顺序出现的计算顺序。比如常见的加法和减法都是left
。higherThan
运算的优先级,乘法运算是优先于加减运算的。除了higherThan
,也支持使用lowerThan
来指定优先级低于某个其他组。 -
2.设置优先级
继承 CustomerPrecedence 优先级组,也可以继承已有优先级组 AdditionPrecedence等
infix operator **: CustomerPrecedence
infix
表示要定义的是一个中位操作符,即前后都是输入;其他的修饰子还包括prefix
和postfix
,感兴趣的可以尝试一下; -
3.在扩展类里写实现,或者自定义类
用
**
运算符定义一个数的平方。extension Int { static func ** (lhs: Int, rhs: Int) -> Int { return Int(pow(Double(lhs), Double(rhs))) } } print(10**2)// 100
字符串示例
precedencegroup BLCustomerPrecedence { /// 优先从左向右, left, right or none associativity: left // higherThan: MultiplicationPrecedence//优先级,比乘法运算高 // lowerThan: AdditionPrecedence // 优先级, 比加法运算低 assignment: false // 是否是赋值运算 } infix operator ~~: BLCustomerPrecedence extension String { static func ~~ (left: String, right: String) -> String { return left + right } } print("12112"~~"~~")
便捷写法:
求一个数的平方根
prefix operator √ // 默认 左->右 的优先级 prefix func √ (number: Double) -> Double { return sqrt(number) } √4 // 2
需要注意的是,Swift
的操作符必须是全局部的,用static
修饰。这就可能与其他 module 定义的操作符有冲突,这点需要特别注意。自定义操作符应该尽量避免重载,应简尽简,切禁写的太复杂,命名尽量规范,避免误解。