基本运算符
运算符是用于检查,更改、合并值的特殊符号或短语。 例如, 加法运算符 (+
) 将两个数字相加, 如 let i = 1 + 2
, 逻辑与运算符 (&&
) 组合两个布尔值, 如 if enteredDoorCode && passedRetinaScan
。
Swift 支持大多数标准 C 语言运算符,并改进了一些功能以消除常见的编码错误。为防止误用等号运算符 (==
), 赋值运算符 (=
)并不返回值。为了避免数值在计算时超出它们类型允许值的范围,出现意外结果,算术运算符 (+
, -
, *
, /
, %
等) 会检测且不允许值溢出。您可以使用 Swift 值溢出运算符来选择值溢出行为,相关描述请参见 溢出运算符。
Swift 还提供了 C 语言中不存在的范围运算符,如 a..<b
和 a...b
,作为表示区间值的快捷方式。
本节仅描述了 Swift 中常见的运算符。 高级运算符 章节会介绍 Swift 高级运算符,并描述了如何自定义运算符,以及如何为您的自定义类型实现标准运算符。
术语
运算符分为一元的、二元的和三元的:
- 一元 运算符运算单个目标值(例如
-a
)。
一元 前缀 运算符显示在目标值之前(例如!b
),而一元 后缀 运算符显示在目标值之后(例如c!
)。 - 二元 运算符运算两个目标值(例如
2 + 3
)并且是 中缀的 因为它们显示在两个目标值之间。 - 三元 运算三个目标值。像 C , Swift 只有一个三元运算符,就是三元条件运算符(
a ? b : c
)。
被运算符影响的值叫做 操作数 。在表达式 1 + 2
中, +
号是二元运算符, 1
和 2
是它的两个操作数。
赋值运算符
赋值运算符 (a = b
)用 b
的值初始化或更新 a
的值
let b = 10
var a = 5
a = b
// a 现在等于 10
如果赋值运算符的右侧是一个包含多个值的元组,那么它的元素可以同时分解为多个常量或者变量:
let (x, y) = (1, 2)
// x 等于 1, y 等于 2
与 C 和 Objective-C 中的赋值运算符不同,Swift 的赋值运算符本身不返回值。下方的语句是无效的:
if x = y {
// 这是无效的,因为 x = y 不返回值
}
当实际应当使用相等运算符(==
)时,这个特性可以防止赋值运算符(=
)被误用。通过使 if x = y
无效,Swift 帮助您避免在代码中出现这类错误。
算术运算符
Swift 对于所有数字类型都支持四种标准的算术运算符:
- 加 (
+
) - 减 (
-
) - 乘 (
*
) - 除 (
/
)
1 + 2 // 等于 3
5 - 3 // 等于 2
2 * 3 // 等于 6
10.0 / 2.5 // 等于 4.0
与 C 以及 Objective-C 不同的是, 在 Swift 中, 默认情况下算术运算符不允许值溢出。但是你能通过用 Swift 的溢出符号 (正如 a &+ b
)去加入值溢出的行为。参考 Overflow Operators。
加号同样也支持 String
的链接:
"hello, " + "world" // 等于 "hello, world"
取余运算符
取余 运算(a % b
)计算出 a
中包含多少个 b
,并且将剩余值返回(即余数 )。
注意
取余运算符 (
%
) 在别的一些语言中的意思是 模运算符 。不过,严格意义上在 Swift 中,对于负数来说它是取余运算而不是模运算。
取余运算是这么计算的。要计算 9%4
,首先要计算 9
除以 4
的商:
你可以将 9
分成两份 4
, 余 1
(如橘黄色所示)。
在 Swift 中, 可以这么写:
9 % 4 // 等于 1
要确定 a % b
的结果,%
算子等价于下面的等式并且将 remainder
作为其返回值:
a
= (b
x some multiplier
) + remainder
some multiplier
是 a
中包含 b
的最大倍数。
将 9
和 4
带入方程式:
9
= (4
x 2
) + 1
当 a
为负数时,方法是一样的:
-9 % 4 // 等于 -1
将 -9
和 4
带入方程式:
-9
= (4
x -2
) + -1
计算出余数 -1
。
可以忽略代数 b
为负值的情况。因为 a % b
与 a % -b
计算的结果是一样的。
一元减号运算符
可以使用前缀 -
( 称为 一元减号运算符)来切换数值的符号:
let three = 3
let minusThree = -three // minusThree 等于 -3
let plusThree = -minusThree // plusThree 等于 3, or "--3"
一元减号运算符(-
)直接位于它运行的值之前,没有任何空格。
一元加号运算符
在 一元加号运算(+
) 只返回其所操作的价值,没有任何变化:
let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
尽管一元加号运算符实际上并没有做任何事情,但是当使用一元减号运算符作为负数时,你可以使用它来为代码提供正数的对称性。
复合赋值运算符
与 C 类似,Swift 也提供了赋值运算符(=
)与另外一个运算符相结合的 复合赋值运算符。加法赋值运算符 (+=
)就是其中一例:
var a = 1
a += 2
// a 现在等于 3
表达式 a += 2
是 a = a + 2
的缩写。将加法运算与赋值运算融合在一个运算中,可以同时高效的完成两个任务。
注意
复合赋值运算符没有返回值。例如,你不能这么写
let b = a += 2
。
Swift 标准库提供的关于运算符信息,你可以参考 运算符表。
比较运算符
Swift 支持所有的标准 C 系比较运算符:
- 等于 (
a == b
) - 不等于 (
a != b
) - 大于 (
a > b
) - 小于 (
a < b
) - 大于等于 (
a >= b
) - 小于等于 (
a <= b
)
注意
Swift 还提供了两个恒等运算符(
===
和!==
),你可以用它们来判断两个对象引用是否指向同一个实例。更多信息见 结构体和类。
每个比较运算符都会返回一个布尔值来表示该语句是否为真。
1 == 1 // true, 因为 1 等于 1
2 != 1 // true, 因为 2 不等于 1
2 > 1 // true,因为 2 大于 1
1 < 2 // true,因为 1 小于 2
1 >= 1 // true,因为 1 大于等于 1
2 <= 1 // false,因为 2 没有小于等于 1
比较运算符通常被用在条件语句中,比如 if
语句:
let name = "world"
if name == "world" {
print("hello, world")
} else {
print("I'm sorry \(name), but I don't recognize you")
}
// 输出 "hello,world",因为 name 的确等于 "world"。
更多关于 if
语句的信息见 控制流。
如果两个元组有着相同数量和类型的元素,你就可以比较它们。元组的比较是从左向右,逐个比较的,直到遇到不相等的元素为止。也就是说,每次元素比较的返回值都决定着整个元组比较的结果。如果所有对应元素都相等,那么这两个元组就是相等的。比如:
(1, "zebra") < (2, "apple") // true,因为 1 小于 2,"zebra" 和 "apple" 没有被比较。
(3, "apple") < (3, "bird") // true,因为 3 等于 3,而且 "apple" 小于 "bird"。
(4, "dog") == (4, "dog") // true,因为 4 等于 4,而且 "dog" 等于 "dog"。
从上边的例子中你可以看到,第一行中的 1 小于 2,所以 (1, "zebra")
小于 (2, "apple")
,"zebra"
是否小于 "apple"
并没有起到任何作用,因为结果已经由第一个元素的比较决定了。当然,如果两个元组的第一个元素相等的话,它们的第二个元素就会被拿来比较——第二行和第三行就为我们展示了这些。
如果一个操作符可以被用来比较两个元组的每个对应元素,那么它就可以被用来比较这两个元组。比如像以下代码演示的那样,你可以比较两个类型为 (String, Int)
的元组,因为 String
和 Int
类型的值都可以用 <
操作符比较。与之形成对比的是,两个 (String, Bool)
类型的元组不能用 <
比较,因为 <
运算符在 Bool
上没有定义。
("blue", -1) < ("purple", 1) // 正确,比较的结果为 true
("blue", false) < ("purple", true) // 错误,因为两个布尔类型的值不能用 < 比较。
注意
Swift 标准库只定义了用于比较拥有七个以内元素的元组的操作符,如果想要比较两个拥有七个或更多元素的元组,你就需要自己来实现该运算符。
三元运算符
三元运算符 是一种有三个部分的特殊运算符,它的形式是 question ? answer1 : answer2
。它简洁的表示了根据 question
是真还是假执行两个表达式其中一个。如果 question
结果是真,则执行表达式 answer1
并返回它的结果;否则的话执行表达式 answer2
并返回它的结果。
三元运算符是对下面这段代码的简写:
if question {
answer1
} else {
answer2
}
这是一个计算表格行高的例子。如果有表头的话行高要比内容高度高50点,没有表头的话要高20点。
let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// 行高等于 90
这个例子是下面代码的简写
let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
rowHeight = contentHeight + 50
} else {
rowHeight = contentHeight + 20
}
// 行高等于 90
第一段代码使用三元运算符使得 rowHeight
可以使用一行代码就得到正确的值,相比第二段代码更加简洁。
三元运算符为有两种情况的判断提供了更加有效和简洁的方式。需要注意,过度使用三元运算符会使代码很难被读懂。所以应该避免在一个组合语句中使用多个三元运算符。
空合运算符
空合运算符 (a ?? b
) 在可选型 a
有值的时候就为它解包,在 a
为 nil
的时候就返回默认值 b
。表达式 a
一定要是可选型。表达式 b
和 a
存储的值类型一定要一致。
空合运算符是下面代码的简写:
a != nil ? a! : b
上面的代码使用了三元运算符为 (a!
) 强制解包,当 a
不是 nil
时返回 a
的值,否则返回 b
。空合运算符为条件判断和解包提供了更优雅的方式同时也提高了代码的可读性。
注意
如果
a
的值不是nil
,那么b
的值将不会被计算。这就是 短路求值。
下面的例子是使用空合运算符来选择默认颜色名还是用户可选型用户自定义颜色名:
let defaultColorName = "red"
var userDefinedColorName: String? // 默认为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 是 nil, 所以 colorNameToUse 被设置为默认值 "red"
变量 userDefinedColorName
被定义为 String
可选型,默认值为 nil
。因为 userDefinedColorName
是一个可选型,就可以使用空合运算符来判断它的值。在上面的例子中使用空合运算符来为变量名为 colorNameToUse
的赋值 String
的值。因为 userDefinedColorName
是 nil
的,表达式 userDefinedColorName ?? defaultColorName
返回 defaultColorName
的值,即 "red"
。
如果你将一个不是 nil
的值赋给 userDefinedColorName
再次执行空合运算符将返回封包在 userDefinedColorName
的值而不是默认值:
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值不是 nil, 所以 colorNameToUse 被赋值为 "green"
区间运算符
Swift 包含的一些 区间运算符,其实是数值区间表达式的缩写。
闭合区间运算符
闭合区间运算符 (a...b
) 表示从 a
到 b
的区间,并且包含 a
和 b
。a
一定不能大于 b
。
当你想遍历一个区间中的值加以利用时,那么闭合区间运算符就在合适不过了,比如在用 for
-in
循环的时候:
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
更多关于 for
-in
循环的信息, 参考 控制流。
半开区间运算符
半开区间运算符(a..<b
)定义了一个从 a
到 b
但不包括 b
的区间。之所以称之为半开,是因为该区间只包含第一个值,而不包含最后一个值。与闭区间运算符一样,a
绝不可以大于 b
。如果 a
等于 b
的话,就表示该区间为空。
当你作用于一个索引从 0 开始的列表(比如数组)时,如果你想要从 0 开始,一直数到(但不过包括)列表的长度,半开区间就显得非常有用了:
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("Person \(i + 1) is called \(names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack
要注意,虽然数组里有 4 个元素,但是 0..<count
只能数到 3
,也就是数组中最后一个元素的索引,这是因为该区间是半开的。更多有关数组的信息,见 Arrays。
单侧区间
闭区间运算符还有另外一种形式,表示向一个方向尽可能地延伸——例如在数组中,一个包含了从索引为 2 开始一直到结尾的所有元素的区间。在这些情况下,你可以省去该区间操作符一侧的值。因为这种区间只有一侧有值,所以它们叫作 单侧区间,例如:
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
半开区间操作符如果只保留其终值就是它的单侧形式了。就像它的完全形式一样,终值本身并不是该区间的一部分,例如:
for name in names[..<2] {
print(name)
}
// Anna
// Alex
单侧区间不仅仅能被用于下标,还能用于一些其它情况。你无法遍历省略了初始值的单侧区间,因为那种形式没有明确指出遍历应该从哪儿开始。不过你可以遍历没有终值的单侧区间;需要注意的是,因为区间的延伸特性,请务必确保你的遍历循环里有一个清晰的用于结束循环的条件。你还可以检查一个单侧区间是否包含某个特定的值,就像下边代码展示的那样。
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
逻辑运算符
逻辑运算符 可以修改或组合布尔逻辑值 true
和 false
。Swift 支持基于 C 语言中的 3 种标准逻辑运算符:
- 逻辑非(
!a
) - 逻辑与(
a && b
) - 逻辑或(
a || b
)
逻辑非运算符
逻辑非运算符 (!a
)对一个布尔值取反,使 true
变成 false
,false
变成 true
。
逻辑非运算符是一个前缀运算符,直接写在操作值的前边,且中间没有空格。它可以读作 「非 a
」,举例如下:
let allowedEntry = false
if !allowedEntry {
print("ACCESS DENIED")
}
// 打印 "ACCESS DENIED"
if !allowedEntry
语句可以读作 「如果不被允许进入」。接下来一行代码只有在 「不被允许进入」为真时,即 allowedEntry
是 false
时才会执行。
在上面的示例代码中,谨慎的选择布尔常量或变量的名字,有助于保持代码的可读性和简洁性,同时也要避免双重否定或者混乱的逻辑语句。
逻辑与运算符
逻辑与运算符 (a && b
) 只有在两个值都为 true
时整个表达式的值才为 true
。
如果任意一个值为 fasle
,整个表达式的值就会为 false
。事实上,如果 第一个 值为 false
,第二个值就不会再被求值了,因为它无论如何都不能让整个表达式的值为 true
。这就是所谓的 短路求值。
这个例子将会考虑两个 Bool
值,只有这两个值都为 true
时才会允许访问:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "ACCESS DENIED"
逻辑或运算符
逻辑或运算符( a || b
)是由两个相邻管道字符组成的中缀运算符。你可以使用它来创建逻辑表达式,表达式的两个值中只要有 一个 值是 true
,则整个表达式为 true
。
与上面的逻辑与运算符一样,逻辑或运算符也使用短路求值原则来判断表达式。如果逻辑或表达式的左侧值是 true
,则不需要继续判断右侧,因为无论右侧的值是什么都不能更改整个表达式的结果。
在下面的例子中,第一个 Bool
值( hasDoorKey
)是 false
,但第二个值( knowsOverridePassword
)是 true
。 因为其中有一个值是 true
,所以整个表达式的结果为 true
,即允许访问:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "Welcome!"
混合逻辑运算
你可以组合多个逻辑运算符来创建更长的复合表达式:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "Welcome!"
此示例中使用多个 &&
和 ||
运算符来创建更长的复合表达式。但是, &&
和 ||
运算符仍然是只操作两个值,所以这个复杂表达式实际上是由三个较小的表达式组合而成。该示例可以理解为:
如果我们输入了正确的门禁密码并通过了视网膜扫描,或者我们有一个正确的门钥匙,或者我们知道紧急密码,那么就允许进入。
基于 enteredDoorCode
、passedRetinaScan
和 hasDoorKey
的值,前两个子表达式的结果是 false
。但是,因为知道紧急密码,所以整个复合表达式的结果仍然是 true
。
注意
Swift 逻辑运算符
&&
和||
都是左关联的,这意味着具有多个逻辑运算符的复合表达式首先判断最左边的子表达式。
显性括号
为了使复杂的表达式更易于阅读,有时候加上括号会有所帮助,即便严格意义上并不是必须的。在门禁的例子中,给复合表达式的首部加上括号,使得其意图显得明确:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
print("Welcome!")
} else {
print("ACCESS DENIED")
}
// 打印 "Welcome!"
括号清楚地表明了前两个值被视为整体逻辑中独立可能状态的一部分。这个复合表达式的结果并没有改变,但是其整体的意图对于读者来说更加清晰了。易读性总是优先于简洁性;在可以帮助你明确意图的地方,使用括号吧。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: