Swift 基本运算符

运算符是用于检查、更改或组合一个或多个值的特殊符号或短语。例如,加法运算符(+)将两个数字相加,如在let i=1+2中所示,逻辑“与”运算符(&&)将两个布尔值合并,如enteredDoorCode && passedRetinaScan

Swift支持我们已经从C等语言中了解到的运算符,并改进了一些功能以消除常见的编码错误。赋值运算符(=)不返回值,以防止在使用相等运算符(==)时错误地使用它。算术运算符(+、-、*、/、%等)检测并禁止值溢出,以避免在处理大于或小于存储类型值范围的数字时出现意外结果。我们可以使用Swift的溢出运算符选择值溢出行为,如溢出运算符中所述。

Swift还提供了在C中没有的范围运算符,例如a…<ba…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 is now equal to 10

如果赋值的右侧是具有多个值的元组,则其元素可以同时被分解为多个常量或变量:

let (x, y) = (1, 2)
// x is equal to 1, and y is equal to 2

与C和Objective-C中的赋值运算符不同,Swift中的赋值运算符本身不返回值。以下语句无效:

if x = y {
    // This is not valid, because x = y does not return a value.
}

此功能可防止在实际使用等于运算符(==)时意外使用赋值运算符(=)。通过使if x=y无效,Swift可以帮助我们避免代码中的此类错误。

算术运算符

Swift支持所有数字类型的四个标准算术运算符:

  • 加法(+)
  • 减法(-)
  • 乘法(*)
  • 除法(/)
1 + 2       // equals 3
5 - 3       // equals 2
2 * 3       // equals 6
10.0 / 2.5  // equals 4.0

与C和Objective-C中的算术运算符不同,Swift算术运算符默认情况下不允许值溢出。我们可以通过使用Swift的溢出运算符(如a &+ b)来选择值溢出行为。请参见溢出运算符
字符串串联也支持加法运算符:

"hello, " + "world"  // equals "hello, world"

余数运算符

余数运算符(a%b)计算出a中将容纳多少个b的倍数,并返回剩余的值(称为余数)。
注意
余数运算符(%)在其他语言中也称为模运算符。然而在Swift中,严格地说,对于负数而言,它是一个余数而不是一个模运算。

下面是余数运算符的工作方式。要计算9%4,首先计算出9内可容纳多少个4:


截屏.png

在9中包含两个4s,其余的是1(以橙色显示)。
在Swift中,这将被写为:

9 % 4    // equals 1

要确定a%b的答案,%运算符计算以下公式并返回余数作为其输出:

a=(b * 因子)+余数

其中,因子是使得b匹配a的最大倍数。
在这个方程中插入9和4得到:

9=(4 x 2)+1

当计算-a的余数时,采用相同的方法:

-9 % 4   // equals -1

在方程中插入-9和4得到:

-9=(4 x -2)+-1

余数值为-1。
如果忽略负值b的符号,那么a%ba%-b总是给出相同的答案。

一元减号运算符

可以使用前缀-,即一元减号运算符来切换数值的符号:

let three = 3
let minusThree = -three       // minusThree equals -3
let plusThree = -minusThree   // plusThree equals 3, or "minus minus three"

一元减号运算符(-)直接在它所操作的值之前加上前缀,没有任何空格。

一元加号运算符

一元加号运算符(+)只返回它所操作的值,没有任何更改:

let minusSix = -6
let alsoMinusSix = +minusSix  // alsoMinusSix equals -6

尽管一元加号运算符实际上什么都不做,但是当一元减号运算符用于负数时,可以使用它在代码中为正数提供对称性。

复合赋值运算符

与C类似,Swift提供复合赋值运算符,将赋值(=)与另一个操作结合起来。比如加法赋值运算符(+=):

var a = 1
a += 2
// a is now equal to 3

表达式a += 2a = a + 2的简写形式。加法和赋值被合并成一个操作符,同时执行两个任务也是有效的。

注意
复合赋值运算符不返回值。例如,不能将b = a += 2写入。

有关Swift标准库提供的运算符的信息,请参阅运算符声明。

比较运算符

Swift支持以下比较运算符:

  • 等于(a == b
  • 不等于(a != b
  • 大于(a > b
  • 小于(a < b
  • 大于或等于(a >= b
  • 小于或等于(a <= b

注意
Swift还提供了两个标识运算符(===!==),用于测试两个对象引用是否都引用同一个对象实例。有关详细信息,请参见标识运算符
每个比较运算符都返回一个Bool值,以表示语句是否为真:

1 == 1   // true because 1 is equal to 1
2 != 1   // true because 2 is not 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 is not 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 recognize you")
}
// Prints "hello, world", because name is indeed equal to "world".

有关if语句的更多信息,请参见控制流

如果两个元组具有相同的类型和相同的元素数量,则可以对它们进行比较。从左到右比较元组,每次比较一个值,直到比较发现两个不相等的值。而这两个值的比较结果决定了元组比较的总体结果。如果所有元素都相等,那么元组本身就是相等的。例如:

(1, "zebra") < (2, "apple")   // true because 1 is less than 2; "zebra" and "apple" are not compared
(3, "apple") < (3, "bird")    // true because 3 is equal to 3, and "apple" is less than "bird"
(4, "dog") == (4, "dog")      // true because 4 is equal to 4, and "dog" is equal to "dog"

在上面的示例中,我们可以在第一行看到从左到右的比较行为。因为1小于2,(1, "zebra")被认为小于(2, "apple"),而不考虑元组中的任何其他值。“zebra”比“apple”大并不重要,因为比较结果已经由元组的第一个元素决定了。但是,当元组的第一个元素相同时,会比较它们的第二个元素,这是在第二行和第三行代码中发生的情况。

只有当元组中的每个元素都可以应用运算符时,元组才能用给定运算符进行比较。例如下面的代码所示,我们可以比较两个类型(String,Int)的元组,因为String和Int值都可以使用<运算符进行比较。相反,类型(String,Bool)的两个元组不能用<运算符进行比较,因为<运算符不能应用于Bool值。

("blue", -1) < ("purple", 1)        // OK, evaluates to true
("blue", false) < ("purple", true)  // Error because < can't compare Boolean values

注意
Swift标准库包含了用于少于7个元素的元组比较运算符。要将元组与七个或更多元素进行比较,必须自己实现比较运算符。

三元条件运算符

三元条件运算符是由三部分组成的特殊运算符,它的形式是疑问句 ? 答案1 : 答案2。它是根据问题是真是假来计算两个表达式之一的快捷方式。如果question为true,它计算answer1并返回其值;否则,它计算answer2并返回其值。

三元条件运算符是以下代码的简写:

if question {
    answer1
} else {
    answer2
}

下面是一个计算表的行高的示例。如果行有标题,行高应比内容高50点,如果行没有标题,行高应比内容高20点:

let contentHeight = 40
let hasHeader = true
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
// rowHeight is equal to 90

上面的例子是下面代码的速写:

let contentHeight = 40
let hasHeader = true
let rowHeight: Int
if hasHeader {
    rowHeight = contentHeight + 50
} else {
    rowHeight = contentHeight + 20
}
// rowHeight is equal to 90

第一个示例使用三元条件运算符,意味着行高可以在一行代码上设置为正确的值,这比第二个示例中使用的代码更简洁。
三元条件运算符提供了一个有效的快捷方式,来决定要计算两个表达式中的哪一个。不过,要小心使用三元条件运算符。如果过度使用,它的简洁性会导致代码难以阅读。我们应该避免将三元条件运算符的多个实例组合到一个复合语句中。

nil聚合运算符

nil聚合运算符(a ?? b) ,如果可选项a包含值,则将其展开;如果a为nil,则返回默认值b。表达式a始终是可选类型。表达式b必须与存储在a中的类型匹配。
nil聚合运算符是以下代码的简写:

a != nil ? a! : b

上面的代码使用三元条件运算符和强制展开(a!),来在当a不为nil时,访问包装在a中的值,否则当a为nil时,就返回b。nil聚合运算符提供了一种更优雅的方式,以简洁易读的形式封装这种条件检查和展开。

注意
如果a的值不是nil,则不计算b的值。这就是所谓的短路评估。

下面的示例使用nil聚合运算符在默认颜色名称和可选的用户定义颜色名称之间进行选择:

let defaultColorName = "red"
var userDefinedColorName: String?   // defaults to nil

var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is nil, so colorNameToUse is set to the default of "red"

userDefinedColorName变量定义为可选字符串,默认值为nil。因为userDefinedColorName是可选类型,所以可以使用nil聚合运算符来考虑其值。在上面的示例中,nil聚合运算符用来确定colorNameToUse这个字符串变量的初始值。因为userDefinedColorName为nil,所以表达式userDefinedColorName ?? DefaultColorName返回DefaultColorName"red"的值。
如果将非nil值赋给userDefinedColorName,并再次执行nil聚合运算符检查,则将使用包装在userDefinedColorName中的值,而不是默认值:

userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName is not nil, so colorNameToUse is set to "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

有关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

请注意,数组包含四个项,但0..<count最多只能计算3(数组中最后一个项的索引),因为它是半开范围。有关数组的详细信息,请参见数组

单边范围

闭合范围运算符具有另一种单边形式,用于在一个方向上尽量连续的范围,例如,从索引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语言中的三种标准逻辑运算符:

  • 逻辑非(!a
  • 逻辑与(a && b
  • 逻辑或(a || b

逻辑非运算符

逻辑非运算符(!a) 反转布尔值,使true变为false,false变为true。
逻辑非运算符是一个前缀运算符,紧跟在它所操作的值之前,没有任何空格。可以将其解读为“非a”,如以下示例所示:

let allowedEntry = false
if !allowedEntry {
    print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"

如果!allowedEntry可以读作“if not allowed entry”。只有当“not allowed entry”为true时,即allowedEntry为false时,才执行下一行。
在本例中,仔细选择布尔常量和变量名有助于保持代码可读性和简洁性,同时避免双重否定或混淆逻辑语句。

逻辑与运算符

逻辑与运算符(a && b)创建逻辑表达式,其中两个值都必须为真,整个表达式才能为真。
如果其中一个值为false,则整个表达式也将为false。实际上,如果第一个值为false,第二个值甚至不会被计算,因为它不可能使整个表达式等于true。这就是所谓的短路评估
本例考虑两个Bool值,仅当两个值都为真时才允许访问:

let enteredDoorCode = true
let passedRetinaScan = false
if enteredDoorCode && passedRetinaScan {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "ACCESS DENIED"

逻辑或运算符

逻辑或运算符(a || b)是由两个相邻的管道字符组成的中缀运算符。我们可以使用它来创建逻辑表达式,其中只有两个值中的一个为真,整个表达式就能为真。
与上面的逻辑“与”运算符一样,逻辑“或”运算符使用短路评估来考虑其表达式。如果逻辑或表达式的左侧为true,则不计算右侧,因为它不能更改整个表达式的结果。
在下面的示例中,第一个Bool值(hasDoorKey)为false,但第二个值(knowsOverridePassword)为true。因为其中一个值为true,所以整个表达式的计算结果也为true,并且允许访问:

let hasDoorKey = false
let knowsOverridePassword = true
if hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

组合逻辑运算符

我们可以组合多个逻辑运算符以创建更长的复合表达式:

if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

本例使用多个&&||运算符创建较长的复合表达式。但是,&&||运算符仍然只对两个值进行操作,因此这实际上是三个较小的表达式链接在一起。这个例子可以理解为:
如果我们输入了正确的门代码并通过了视网膜扫描,或者我们有一把有效的门钥匙,或者如果我们知道紧急超控密码,那么允许进入。
根据enteredDoorCode、passedRetinaScan和hasDoorKey的值,前两个子表达式为false。但是,knowsOverridePassword是true的,因此整个复合表达式的计算结果仍然是true。

注意
Swift逻辑运算符&&||是左关联的,这意味着具有多个逻辑运算符的复合表达式首先计算最左边的子表达式。

显式括号

有时,在不严格需要括号的情况下包含括号是很有用的,这样可以使复杂表达式的所要表达的意思更易于阅读。在上面的door access示例中,在复合表达式的第一部分周围添加括号以明确其意图非常有用:

if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
    print("Welcome!")
} else {
    print("ACCESS DENIED")
}
// Prints "Welcome!"

括号清楚地表明,前两个值被认为是整个逻辑中一个单独的可能状态的一部分。复合表达式的输出并没有改变,但总体意图对读者来说更为清晰。易读性总是优于简洁性;我们可以在有助于表达意图的地方使用括号。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 运算符是检查、改变、合并值的特殊符号或短语。例如,加号+将两个数相加(如let i = 1 + 2)。更复杂的运算...
    穷人家的孩纸阅读 14,877评论 3 6
  • Swift 的运算符和C以及OC的运算符相比并没有太大的变化,但是有很好的补充。 赋值运算符(因为元组的使用更加丰...
    雪_晟阅读 665评论 0 0
  • 简介 Swift 支持大部分标准 C 语言的运算符,且为了减少常见编码错误做了部分改进。 如:赋值符(=)不再有返...
    DevXue阅读 317评论 0 0
  • Swift中文文档 运算符分为一元、二元和三元运算符 一、赋值运算符 赋值运算符: = 例如: (a = b), ...
    伯wen阅读 609评论 0 0
  • 术语 运算符分为一元、二元和三元运算符: 一元运算符对单一操作对象操作(如 -a)。一元运算符分前置运算符和后置运...
    CoderLGL阅读 578评论 0 1