该文章翻译自Apple官方文档: The Swift 4 Programming Language
Guards翻译组 正在翻译Swift 4的全套文档,这是该文档第二章节《Basic Operators》的下半部分,上半部分在这里,第一章节《The Basics》 上半部分请点这里/下半部分请点这里,原文链接: Basic Operators
校对 : Stevin三天三夜 (Blog / GitHub)
译者心声
我们会不定期的更新翻译文章,如感兴趣,可以关注<我们的简书,获取翻译组更多文章>
我们是一群热爱翻译并且热爱 Swift 的人, 希望通过自己的努力让不熟悉英语的程序员也能学习到国外的高质量的文章. 如发现文章中翻译错误之处,烦请跟我们联系,我们第一时间更正.
想成为我们中一员? 是的,我们正在招募对翻译感兴趣的小伙伴,如果你想提高阅读英语文档的水平,如果你不甘寂寞,如果你想为广大开发者做点事. QQ:835087646
本篇包含内容:
- 空合运算符
- 区间运算符
- 逻辑运算符
空合运算符(Nil-Coalescing Operator)
空合运算符(a ?? b
)会对可选类型a
尝试解封,如果a
包含一个值就进行解封,如果a
为nil
则返回一个默认值b
。表达式a
必须是一个可选(Optional
)类型。表达式b
的类型必须跟a
存储的值的类型一致。
空合运算符是对下面的代码的简短表达:
a != nil ? a! : b
上述代码使用了三目运算符。当可选类型a
的值不为空时,进行强制解封(a!
)以访问a
的值,否则返回默认值b
。空合运算符提供了一种更优雅的方式去封装条件判断和解封两种行为。
NOTE:
如果
a
为非空值(non-nil
),那么值b
不会进行计算。这就是所谓的短路求值。
下面的例子使用空合运算符,实现了在默认颜色名和可选自定义颜色名之间的选择:
let defaultColorName = "red"
var userDefinedColorName: @H_301_108@String? // 默认值为 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 值为 nil,所以 colorNameToUse 值被设置为 "red"
userDefinedColorName
变量被定义为一个可选的String
类型,默认值为nil
。因为userDefinedColorName
是一个可选类型,你可以使用空合运算符来判断它的值。在上一个例子中,空合运算符被用于初始化一个名为colorNameToUse
字符串(String
)类型变量。因为userDefinedColorName
值为nil
,所以表达式userDefinedColorName ?? defaultColorName
会返回defaultColorName
的值,即"red"
。
如果你分配一个非空值给userDefinedColorName
,再次执行空合运算,运算结果为userDefinedColorName
解封之后的值而不是默认值(defaultColorName
)。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值非 nil,所以 colorNameToUse 的值被设置为 "green"
区间运算符(Range Operators)
Swift 提供几个方便表达一个区间的值的区间运算符。
闭区间运算符
闭区间运算符(a...b
)定义一个包含a
和b
(包括a
和b
)的所有值的区间。a
的值不能超过b
的值。
闭区间运算符在遍历一个区间的所有值时是非常有用的,如在for-in
循环中:
for index in 1...5 {
print("\(index) times 5 is \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
关于for-in
循环,请查阅Control Flow。
半开区间运算符
半开区间运算符(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])")
}
// 第1个人叫 Anna
// 第2个人叫 Alex
// 第3个人叫 Brian
// 第4个人叫 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
逻辑运算符(Logical Operators)
逻辑运算符的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算符:
- 逻辑非(
!a
) - 逻辑与(
a && b
) - 逻辑或(
a || b
)
逻辑非运算符
逻辑非运算符(!a
)对一个布尔值取反,使true
变false
,使false
变true
。
逻辑非运算符是一个前置运算符,需要跟在操作数之前,且不加空格。读作“非a”
,例子如下:
let allowedEntry = false
if !allowedEntry {
@H_301_108@print("ACCESS DENIED")
}
@H_696_403@// 输出 "ACCESS DENIED"
语句if ! allowedEntry
可以读作“如果非allowedEntry
”。接下一行代码只有在“非allowedEntry
”时为true
,即allowedEntry
为false
时被执行。
在上述例子中,小心地选择布尔常量或变量有助于代码的可读性,同时应避免使用双重逻辑非或混乱的逻辑语句。
逻辑与运算符
逻辑与运算符(a && b
)表达式只有在a
和b
的值都为true
时,整个表达式才会是true
。
只要任一值为false
,整个表达式的值为false
。事实上,如果第一个值为false
,则第二个值将不被运算,因为它不可能影响整个表达式的结果。这被称为*短路计算(short-circuit evaluation)*
。
如下例,只有两个Bool
值都true
时才允许进入if:
let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
@H_301_108@print("Welcome!")
} else {
@H_301_108@print("ACCESS DENIED")
}
@H_696_403@// 输出 "ACCESS DENIED"
逻辑或运算符
逻辑或运算符(a || b
)是一个由两个连续的|
组成的中置运算符。它表示了两个逻辑表达式的其中一个值为true
,整个表达式就为true
。
与逻辑与运算符类似,逻辑或运算符也是“短路计算”的。当左侧的表达式为true
时,将不计算右侧表达式,因为它不会改变整个表达式的值。
在下面的例子中,第一个Bool
值(hasDoorKey
)为false
,但第二个值(knowsOverridePassword
)为true
。因为其中一个值为true
,所以整个表达式为true
,并且允许进入:
let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
@H_301_108@print("Welcome!")
} else {
@H_301_108@print("ACCESS DENIED")
}
@H_696_403@// 输出 "Welcome!"
逻辑运算符组合计算
你可以组合多个逻辑运算符来表达一个复合逻辑:
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
@H_301_108@print("Welcome!")
} else {
@H_301_108@print("ACCESS DENIED")
}
// 输出 "Welcome!"
在这个例子中使用了多个&&
和||
运算符来创建一个长复合逻辑表达式。然而,&&
和||
运算符始终只能操作两个值,所以这实际上是三个简单逻辑连续操作的结果。我们来解读一下:
如果我们输入正确的密码(enteredDoorCode
)并通过了视网膜扫描(passedRetinaScan
),或者有一个有效的钥匙(hasDoorKey
),又或者知道紧急情况下重置的密码(knowsOverridePassword
),我们就能把门打开并进入。
基于enteredDoorCode
、passedRetinaScan
和hasDoorKey
的值,前两个简单逻辑的结果是false
,但我们知道紧急情况下重置的密码(knowsOverridePassword
),所以整个表达式的值还是true
。
NOTE
Swift逻辑操作符
&&
和||
是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
使用括号以明确优先级
为了使一个复合表达式更易理解,有时使用括号来明确优先级是有效的,即使它并不是必要的。在关于门的权限的例子中,给第一部分加上括号,使它看起来逻辑更明确:
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
@H_301_108@print("Welcome!")
} else {
@H_301_108@print("ACCESS DENIED")
}
// 输出 "Welcome!"
这个括号使前两个值被看成整个逻辑表达式中独立的一部分。虽然整个表达式的结果是一样的,但对于读代码的人而言有括号的更易读懂。可读性比简洁性更重要,请在可以让你代码变清晰的地方加个括号吧。