这篇笔记也是同步 Swift 6 官方教程中的第三篇 《Basic Operators》,主要Swift中涉及到的基础运算符,如果你已经有了其他语言的编程基础,那么理解这一章节的难度很小。
- 官方教学文档 《Basic Operators》
运算符是用于检查、更改或组合值的特殊符号或短语。例如,加法运算符 +
将两个数字相加,例如 let i = 1 + 2
;逻辑与运算符 &&
将两个布尔值合并,例如 if enteredDoorCode && passingRetinaScan
。
Swift 支持 C 语言等语言中的运算符,并改进了多项功能以消除常见的编码错误。赋值运算符 =
不返回值,以防止在需要使用等于运算符 ==
时被误用。算术运算符 +、-、*、/、%
等会检测并禁止值溢出,以避免在处理大于或小于存储它们的类型允许值范围的数字时出现意外结果。可以使用 Swift 的溢出运算符来选择值溢出行为。
Swift 还提供了 C 语言中没有的范围运算符,例如 a..<b
和 a...b
,作为表示值范围的快捷方式。
本章介绍 Swift 中的常用运算符。高级运算符涵盖了 Swift 的高级运算符,并描述了如何定义自定义运算符以及如何为自定义类型实现标准运算符。
1. Terminology 术语定义
运算符分为一元运算符、二元运算符和三元运算符:
- 一元运算符作用于单个目标(例如
-a
),一元前缀运算符紧接在其目标之前(例如!b
),一元后缀运算符紧接在其目标之后(例如c!
) - 二元运算符作用于两个目标(例如
2 + 3
),并且是中缀运算符,因为它们位于两个目标之间。 - 三元运算符作用于三个目标,Swift 只有一个三元运算符,即三元条件运算符(
a ? b : c
)。
运算符影响的值称为操作数。在表达式 1 + 2
中,+
符号是中缀运算符,它的两个操作数分别是值 1
和 2
。
2. Operator 运算符
1.2 Assignment Operator 赋值运算符
Swift 支持所有数字类型的四种标准算术运算符:
- 加法
+
; - 减法
-
; - 乘法
*
; - 除法
/
;
1 + 2 // 3
5 - 3 // 2
2 * 3 // 6
10.0 / 2.5 // 4.0
与 C 和 Objective-C 中的算术运算符不同,Swift 的算术运算符默认 不允许值溢出。可以使用 Swift 的溢出运算符 a &+ b
来启用值溢出行为。
字符串连接也支持加法运算符:
"hello, " + "world"
1.3 Remainder Operator 余数运算符
余数运算符 a % b
计算出 a
中可以容纳多少个 b
的倍数,并返回剩余的值。
9 % 4 // equals 1
-9 % 4 // equals -1
1.4 Unary Minus Operator 一元减运算符
可以使用前缀 -
来切换数值的符号,称为一元减运算符:
let three = 3
let minusThree = -three // -3
let plusThree = -minusThree // 3
1.5 Unary Plus Operator 一元加运算符
一元加法运算符 +
仅返回其运算的值,不做任何更改:
let minusSix = -6
let alsoMinusSix = +minusSix // -6
1.6 Compound Assignment Operators 复合赋值运算符
与 C 语言类似,Swift 也提供了复合赋值运算符,将赋值运算符 =
与另一个运算符组合在一起:
var a = 1
a += 2 // 3
1.7 Comparison Operators 对比运算符
Swift 支持以下比较运算符:
- 等于
a == b
; - 不等于
a != b
; - 大于
a > b
; - 小于
a < b
; - 大于等于
a >= b
; - 小于等于
a <= b
;
每个比较运算符都返回一个布尔值来指示语句是否为真:
1 == 1 // true because 1 is equal to 1
2 != 1 // true because 2 isn't equal to 1
2 > 1 // true because 2 is greater than 1
1 < 2 // true because 1 is less than 2
1 >= 1 // true because 1 is greater than or equal to 1
2 <= 1 // false because 2 isn't less than or equal to 1
比较运算符经常用于条件语句中,例如 if
语句:
let name = "world"
if name == "world" {
print("hello, world")
}else{
print("I'm sorry \(name), but I don't recongize you")
}
// hello, world
如果两个元组具有相同的类型和相同的值个数,则可以进行比较。元组从左到右逐个值进行比较,直到比较发现两个不相等的值。比较这两个值,比较结果决定了元组比较的总体结果。如果所有元素都相等,则元组本身相等。例如:
(1, "zebra") < (2, "apple") // true
(3, "apple") < (3, "bird") // true
(4, "dog") == (4, "dog") // true
("blue", -1) < ("purple", 1) // OK
("blue", false) < ("purple", true) // 报错
1.8 Ternary Conditional Operator 三元条件运算符
三元条件运算符是一个特殊的运算符,由三部分组成,其形式为问题 ? 答案 1 : 答案 2。它是一种快捷方式,用于根据问题为真或为假来求两个表达式中的一个。如果问题为真,则求答案 1 的值并返回其值;否则,求答案 2 的值并返回其值。
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
上面的代码实际上是下面的代码:
let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader{
rowHeight = contentHeight + 50
}else{
rowHeight = contentHeight + 20
}
1.9 Nil-Coalescing Operator 零合并运算符
空值合并运算符 a ?? b
会在可选类型 a
包含值时将其解包;如果 a
为 nil
,则返回默认值 b
。表达式 a
始终为可选类型。表达式 b
必须与 a
中存储的类型匹配。
let a = 20
let b = 30
a != nil ? a! : b
上面的代码使用了三元条件运算符和强制解包 a!
,当 a
不为 nil
时,访问 a
中包装的值;否则返回 b
。nil
合并运算符提供了一种更优雅的方式,以简洁易读的形式封装了这种条件检查和解包。
下面的示例使用 nil-coalescing
运算符在默认颜色名称和可选的用户定义颜色名称之间进行选择:
let defaultColorName = "red"
var userDefinedColorName: String? // nil
var colorNameToUse = userDefinedColorName ?? defaultColorName // red
userDefinedColorName
变量定义为可选字符串默认值为 nil
。由于 userDefinedColorName
是可选类型,因此可以使用 nil
合并运算符来计算其值。在上面的示例中,该运算符用于确定名为 colorNameToUse
的字符串变量的初始值。由于 userDefinedColorName
为 nil
,因此表达式 userDefinedColorName ?? defaultColorName
返回 defaultColorName 的值
,即 red。
1.10 Range Operators 范围运算符
1.10.1 Closed Range Operator 闭区间运算符
闭合范围运算符 a...b
定义从 a
到 b
的范围,包含值 a
和 b
,要求 a
的值不得大于 b
。闭合范围运算符在迭代要使用范围内所有值时非常有用,例如在 for-in
循环中:
for index in 1...5{
print("\(index) times 5 is \(index * 5)")
}
1.10.2 Half-Open Range Operator 半开范围运算符
半开范围运算符 a..<b
定义一个从 a
到 b
的范围但不包括 b
。与闭区间运算符一样,a
的值不能大于 b
。如果 a
的值等于 b
,则结果范围为空。半开范围在处理从零开始的列表(例如数组)时特别有用,因为在这种情况下,计数到(但不包括)列表的长度非常有用:
let names = ["Anna", "Alex", "Brain", "Jack"]
let count = names.count
for i in 0..<count{
print("Person \(i+1) is called \(names[i])")
}
1.10.3 One-Sided Ranges 单边范围
闭合范围运算符还有另一种形式,用于表示尽可能沿一个方向延伸的范围——例如,包含数组中从索引 2
到数组末尾的所有元素的范围。在这种情况下,可以省略范围运算符一侧的值。这种范围被称为单侧范围,因为运算符仅在一侧有值。例如:
let names = ["Anna", "Alex", "Brain", "Jack"]
for name in names[2...]{
print(name)
}
print("-------------------")
for name in names[...2]{
print(name)
}
半开范围运算符也有一个单侧形式,只写出其最终值。就像在两边都包含一个值一样,最终值不属于范围。例如:
let names = ["Anna", "Alex", "Brain", "Jack"]
for name in names[..<2]{
print(name)
}
单侧范围不仅可以用于下标,还可以用于其他上下文,无法对省略了第一个值的单侧范围进行迭代,因为迭代应该从哪里开始并不明确;但可以对省略了最后一个值的单侧范围进行迭代,由于该范围无限延伸,请确保为循环添加明确的结束条件;还可以检查单侧范围是否包含特定值,如下面的代码所示。
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // false
1.11 Logical Operators 逻辑运算符
逻辑运算符用于修改或组合布尔逻辑值 true 和 false。Swift 支持 C 语言中的三种标准逻辑运算符:
- 逻辑非
!a
; - 逻辑与
a && b
; - 逻辑或
a || b
;
1.11.1 Logical NOT Operator 逻辑非运算符
逻辑非运算符 !a
对布尔值进行取反,使 true 变为 false,false 变为 true。逻辑非运算符是一个前缀运算符,它紧接在其运算值的前面,中间没有空格。它可以读作“not a”,如下例所示:
let allowedEntry = false
if !allowedEntry{
print("Access Denied")
}
1.11.2 Logical AND Operator 逻辑与运算符
逻辑与运算符 a && b
创建的逻辑表达式必须两个值都为真,整个表达式才为真。如果其中一个值为假,整个表达式也为假。如果第一个值为假,第二个值甚至不会被求值,因为它不可能使整个表达式等于真。这被称为短路求值。此示例考虑两个 Bool 值,只有当两个值都为真时才允许访问:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan{
print("Welcome")
}else{
print("Access Denied")
}
1.11.3 Logical OR Operator 逻辑或运算符
逻辑或运算符 a || b
是由两个相邻的竖线字符组成的中缀运算符。可以使用它来创建逻辑表达式,其中两个值中只要有一个为真,整个表达式就为真。与上面的逻辑与运算符类似,逻辑或运算符使用短路求值来计算其表达式。如果逻辑或表达式的左侧为真,则右侧不会被求值,因为它无法改变整个表达式的结果。
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword{
print("Welcome")
}else{
print("Access Denied")
}
1.11.4 Combining Logical Operators 组合逻辑运算符
可以组合多个逻辑运算符来创建更长的复合表达式:
let enteredDoorCode = true
let passedRetinaScan = false
let hasDoorKey = false
let knowsOverridePassword = true
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword{
print("Welcome")
}else{
print("Access Denied")
}
1.11.5 Explicit Parentheses 显式括号
有时即使不是必须使用括号,为了使复杂表达式的意图更易于阅读,使用括号也是很有用的。在上面门禁的例子中,在复合表达式的第一部分添加括号,可以使其意图更明确:
let enteredDoorCode = true
let passedRetinaScan = false
let hasDoorKey = false
let knowsOverridePassword = true
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword{
print("Welcome")
}else{
print("Access Denied")
}