运算符(Operator)是用来检查,改变或合并值的一种特殊符号或短语。例如,加号运算符让两个数字相加(如:let i = 1 + 2),还有些更复杂的运算符,如逻辑与运算符(&&)(如:if enteredDoorCode && passedRetinaScan)和自增运算符(++i)(将i的值加1的便捷写法)。
Swift 支持标准C语言的大多数运算符,并且改进了一些特性以规避常见的代码错误。赋值运算符(=)是没有返回值的,这样是为了避免在使用等于运算符(==)时误用了赋值运算符(=)。算术运算符(+,-,*,/,% 等等)会侦测并阻止值溢出,可避免在运算时超出存储类型的值域范围(比实际运算结果大或小,或不精准--Joe.Huang)。如果需要支持溢出行为,可以选用 Swift 的溢出运算符,详情可见溢出运算符(后面章节译到)。
与C语言不同的是,Swift 允许对浮点数求余(%)。Swift 还提供了C语言所没有的两个区间运算符(a..b和a...b),作为表示值域范围的便捷写法。
本章介绍 Swift 中的常用运算符。高级运算符(后面章节译到)一章涵盖了 Swift 中的高级运算符,并讲解了如何自定义运算符,以及让标准运算符支持自定义类型的运算。
运算符术语
运算符分为一元,二元,三元运算符:
· 一元运算符(unaryoperator)对单个目标进行运算(如-a)。一元运算符前缀(unaryPRefix operator)紧跟运算目标之前(如!b),而一元运算符后缀(unary postfix operator)则紧跟运算目标之后(如i++)。
· 二元运算符(binaryoperator)对两个目标进行运算(如2 + 3),它们属于中缀(infix)运算符,因为(运算符号)出现在两个运算目标之间。
· 三元运算符(ternaryoperator)对三个目标进行运算。与 C 语言一样,Swift 只有一个三元运算符:三元条件运算符(即a ? b : c)。
运算符操作的值称为运算元(operands)。在表达式1 + 2中,+符号是二元运算符,它的两个运算元为值1与值2。
赋值运算符
赋值运算符(assignment operator,a = b)用b的值初始化或更新a的值:
1let b =102var a =53a =b4//a is now equal to 105//a现在等于10
如果赋值语句的右侧是一个包含多个值的元组,元组的元素可一次性用多个常量或变量分解提取出来(上一章讲解元组时提到过分解元组值的方法--Joe.Huang):
1let (x, y) = (1,2)2//x is equal to 1, and y is equal to 23//x等于1,y等于2
与 C 或 Objective-C 语言的赋值运算符不同,Swift 语言的赋值运算符本身没有返回值。因此下面的语句不正确:
1ifx =y {2//this is not valid, because x = y does not return a value3//这是无效的,因为 x = y 不会返回一个值 (x==y才可以,--Joe.Huang)4}
该特性可避免在使用等于运算符(==)时误用了赋值运算符(=)。通过否认if x = y的有效性,Swift 将帮助你规避代码中出现这样的错误。
算术运算符
Swift支持对所有数字类型使用四种标准算术运算符:
·加:+
·减:-
·乘:*
·除:/
11+2//等于325-3//等于232*3//等于6410.0/2.5//等于4.0
与 C / Objective-C 语言的算术运算符不同,Swift 的算术运算符默认不允许值溢出。如果需要支持溢出行为,可以选用 Swift 的溢出运算符(如,a &+ b),详情可见 溢出运算符(后面章节译到)。
加号运算符也支持String拼接:
"hello,"+"world"//等于 "hello, world"
可以将两个Character (字符,Unicode字符--Joe.Huang)值相加,或将一个Character值与一个String值相加,得到新的String值:
1let dog: Character =""2let cow: Character =""3let dogCow = dog +cow4//dogCow 等于 ""
详见字符与字符串拼接(后面章节译到):
求余运算符
求余运算符(remainder operator,a % b)求出 a 包含多少个 b,并返回剩余的值(即整除后的余数remainder)。
注:
求余运算符(%)在其他语言中也称作求模运算符(modulo operator)。但对负数的运算结果表明:Swift 语言的实现是严格的求余操作,而非求模操作。
求余运算符的原理如下。 要计算9 % 4,首先要求出9里面包含多少个4:
如图所示,9里面包含两个4,余数是1(橙色部分)。
Swift中的写法如下:
9%4//等于 1
要求出a % b的结果,%运算符会计算下面的等式,并将余数作为输出结果返回:
a = (b × some multiplier) + remainder
其中some multiplier是a中能装下b的最大数目。
把9和4代入等式:
9= (4×2) +1
a为负数时,求余方法不变:
-9%4//等于 -1
把-9和4代入等式:
-9= (4× -2) + -1
得到的余数值为-1。
b为负值(-b)时,b的负号将被忽略。因此a % b与a % -b 总是返回相同的结果。
浮点数的求余计算
与 C / Objective-C 语言的余数运算符不同,Swift 的余数运算符还能对浮点数进行求余计算:
8%2.5//equals 0.5
上例中,8除以2.5等于3,余数为0.5,因此余数运算符返回Double型的值 0.5。
自增与自减运算符
与 C 语言类似,Swift 也提供了自增运算符(++)与自减运算符(--),作为将数字变量的值加上或减去1的便捷写法。任何整型或浮点型的变量都可以使用这两个运算符。
1var i =02++i//i 现在等于 1
每次调用++i时,i的值就会增加1。本质上,++i就是i = i + 1的便捷写法。类似地,--i也相当于i = i - 1。
++与--两对符号可以作为前缀或后缀运算符使用。++i与i++均可用来将i的值加1。类似地,--i与i--均可用来将i的值减去1。
注意,这些运算符不仅会改变i的值,还会返回一个值。如果你只需要将自增或自减后的值存放在i中,那你可以忽略运算符的返回值。但如果你确实要用到返回值,要注意前缀及后缀运算符返回值的差异,规则如下:
·如果运算符在变量名前面,先改变它的值,再返回其值。
·如果运算符在变量名后面,先返回原值,再改变它的值。
如例:
1var a =02let b = ++a3//a 和 b 现在都等于 1,即改变a的值,再返回4let c = a++5//a 现在等于 2, 但 c 还是自增前的值 1,即先返回的a的原值,再改变其值
在上例中,let b = ++a先增加a的值(加1),然后才返回它的值。因此a与b都等于新的值1。
但是,let c = a++先返回a的原值(加1之前的值),然后才增加a的值。即c得到了原值1,然后a被更新为新值2。
除非你需要利用i++的这一特性,建议你在所有情况下都使用++i与--i,因为它们先修改i再返回修改后的值的动作更符合逻辑。
一元减号运算符
数值前加上前缀-,这个前缀运算符-就称为一元减号运算符:
1let three =32let minusThree = -three//minusThree 等于 -33let plusThree = -minusThree//plusThree 等于 3, 或等于 "减去 minusThree"
一元减号运算符(-)紧靠所要操作的值之前,无需任何空格。
一元加号运算符
一元加号运算符(+)直接返回所操作的值,不作任何处理:
1let minusSix = -62let alsoMinusSix = +minusSix//alsoMinusSix 等于 -6
尽管一元加号运算符实际上不作任何运算,代码中仍然可以用它(提供语义信息,一元减号运算符表示负数,一元加号运算符表示正数--Joe.Huang)与表示负数的一元减号运算符形成对比。
复合赋值运算符
与 C 语言类似,Swift 也提供复合赋值运算符(compound assignment operator)将赋值运算符(=)与其他运算组合起来使用。例如加法赋值运算符(addition assignment operator,+=):
1var a =12a +=23//a 等于 3
上例中的表达式a = a + 2简写为a += 2,加法和赋值两项操作组合为一个单项操作,执行时非常高效。
注:
复合赋值操作没有返回值,即,你不能写:let b = a += 2,这一操作与前面所提到的自增自减操作是不同的。
复合赋值运算符的完整列表可在 表达式 一章中找到(后面章节译到)。
比较运算符
Swift支持标准C 的比较运算符:
等于 (a==b)
不等于 (a!=b)
大于 (a>b)
小于 (a<b)
大于等于 (a>=b)
小于等于 (a<=b)
注:
Swift还提供了恒等(===)和不恒等(!==)两个鉴别运算符,你可以用它测试两个对象是否引用了同一个对象实例。更多详情请参考 类和结构 一章(后面章节译到)。
每个比较运算符都会返回一个Bool值,检测表达式是否成立:
11==1//true, 因为 1 等于 122!=1//true, 因为 2 不等于 132>1//true, 因为 2 大于 141<2//true, 因为 1 小于 251>=1//true, 因为 1 大于等于 162<=1//false, 因为 2 大于等于 1
比较运算符常见于条件表达式中,如 if 条件句:
1let name ="world"2ifname =="world"{3println("hello, world")4}else{5println("I'm sorry \(name), but I don't recognize you")6//输出(”对不起\name,我不认识你“)7}8//prints "hello, world", because name is indeed equal to "world"9//输出 "hello world",因为 name 确实等于"world"
if 语句的更多介绍,详见 流程控制 一章(后面章节译到)。
三元条件运算符
三元运算符是一种特殊运算符,由三个部分组成,表现形式为:question ? answer1 : answer2。它是一种求值简写:根据 question 是否成立,从两个表达式中取出一个并求值。
如果question成立,则计算 answer1的结果并返回其值;否则计算answer2并返回其值。
三元条件运算符是如下代码的缩写:
1ifquestion {2answer13}else{4answer25}
下面的例子将计算表格某行的像素高度。如果该行有表头,则行高应比内容高度高 50 个像素;如果没有表头,则只高出 20 个像素:
1let contentHeight =402let hasHeader =true3let rowHeight = contentHeight + (hasHeader ?50:20)4//rowHeight(行高) 等于 90
上例便是如下代码的缩写:
1let contentHeight =402let hasHeader =true3var rowHeight =contentHeight4ifhasHeader {5rowHeight = rowHeight +506}else{7rowHeight = rowHeight +208}9//rowHeight(行高) 等于 90
使用三元条件运算符的例子说明,可以仅用一行代码就将行高设为正确的值。这比(不用三元运算符的)第二个例子简洁得多,并且行高(rowHeight)无需定义为变量,因为不再需要用if语句修改其值。
三元条件运算符提供了二选一的高效写法。但使用三元条件运算符应小心。如果过度使用,其简明性可能导致代码可读性差。应避免将多个三元条件运算符组合在同一个语句中。
区间运算符
Swift有两个区间运算法,是表示值域的简便写法。
闭区间运算符
区间运算符(a...b)定义了a到b的区间范围,包括a和b 在内。
闭区间运算符在需要遍历某区间内所有的值时很有用,如在for-in循环中使用:
1forindexin1...5{2println("\(index) times 5 is \(index * 5)")3//输出("\(index)乘以 5 得 (\index * 5)")4}5//1 乘以 5 得 56//2 乘以 5 得 107//3 乘以 5 得 158//4 乘以 5 得 209//5 乘以 5 得 25
for-in 语句的更多介绍,详见 流程控制 一章(后面章节译到)。
半闭区间运算符
半闭区间运算符(a..b)定义了从a到b的区间,但b不包括在内。说它是半闭区间,是因为第一个值包含在区间内,但最后一个值在区间外。
半闭区间在处理从 0 开始计数的列表时有用,如遍历数组,可从 0 数到列表的长度(但不包括长度值本身):
1let names = ["Anna","Alex","
Brian","Jack"]2let count =names.count3foriin0..count {4println("Person \(i + 1) is called \(names[i])")5}6//Person 1 名字是 Anna7//Person 2 名字是 Alex8//Person 3 名字是 Brian9//Person 4 名字是 Jack
注意,数组包含四个元素,但因为是半闭区间,所以0..count 只数到3(数组中最后一个元素的索引号)。数组更多信息详见数组 一章(后面章节译到)。
逻辑运算符
逻辑运算符是对布尔逻辑值true或false的组合操作,Swift 支持 C 及其衍生语言的三种标准逻辑运算符:
·逻辑非(!a)
·逻辑与(a && b)
·逻辑或(a || b)
逻辑非运算符
逻辑非运算符对布尔值取反,即 true变成false,false变成true。
逻辑非运算符是一个前缀运算符,紧跟在所操作值的前面,没有任何空格符。可以理解为"非",如下例:
1let allowedEntry =false2if!allowedEntry {3println("accessDENIED")4}5//prints "ACCESS DENIED"6//输出”ACCESS DENIED“
代码中的if !allowedEntry 可以理解为“如果不允许进入”。随后的下一行代码仅当“不允许进入”成立时才会执行;即allowedEntry 为false时才执行。
如上例,布尔值常量及变量的名称应谨慎选择命名,方可确保代码简明又具可读性,同时也应避免使用双重否定或引起混淆的逻辑语句。
逻辑与运算符
(a && b)构造这样的逻辑表达式:运算符两侧的值均为true,整个表达式的求值结果才为true。
如果有一个值为false,整个表达式便为false。事实上,如果第一个值为false,第二个值将不执行求值运算,因为无论它为何值,整个表达式的值都不可能等于true。这也被称为短路求值(short-circuit evaluation)。
下面的例子验证两个值,当两个值都为true时才能访问:
1let enteredDoorCode =true2let passedRetinaScan =false3ifenteredDoorCode &&passedRetinaScan {4println("Welcome!")5}else{6println("ACCESS DENIED")7}8//prints "ACCESS DENIED" 输出"ACCESS DENIED"
逻辑或运算符
(a || b)属于中缀运算符,由两个连续的竖线构成。它用来构造这样的表达式:当两个值中有一个为 true时,整个表达式为true。
与前面的逻辑与运算符一样,逻辑或运算符在检测它的两个表达式时,也使用“短路求值”法。只要逻辑或表达式左侧为true,其右侧将不执行求值运算,因为这时右侧的值对整个表达式的结果不再有影响。
下例中,第一个Bool值(hasDoorKey)为false,但第二个值(knowOverridePassWord)为true。因为有一个值为true,所以整个表达式的求值结果也为true,因此允许访问:
1let hasDoorKey =false2let knowsOverridePassword =true3ifhasDoorKey ||knowsOverridePassword {4println("Welcome!")5}else{6println("ACCESS DENIED")7}8//prints "Welcome!" 输出"Welcome!"
组合使用逻辑运算符
可以将多个逻辑运算符组合起来构成一个较长的复合表达式。
1“ifenteredDoorCode && passedRetinaScan || hasDoorKey ||knowsOverridePassword {2println("Welcome!")3}else{4println("ACCESS DENIED")5}6//prints "Welcome!" 输出"Welcome!"
本例使用多个&&及||运算符构成一条较长的复合表达式。不过,&&与||运算符操作的仍然是两个值,因此该组合表达式实际上是由三个较短的表达式连立而成。它可以这样理解:
如果我们输入了正确的门禁密码、并且通过了视网膜扫描;或者如果我们有门钥匙;或者如果我们知道紧急的备用密码,则允许访问。
根据enteredDoorCode、passedRetinaScan、hasDoorKey 三个常量推算,前两个小表达式的值均为false。不过我们知道紧急的备用密码(knowsOverridePassword 为 true),因此整个复合表达式的求值结果仍然为true。
显式括号
有时(从语法来看)括号并不是必需的,但加上括号却很有用,它可以让复杂表达式的易于阅读。 在上例中,为组合表达式的第一部分加上括号,可使其意图更明显:
1if(enteredDoorCode && passedRetinaScan) || hasDoorKey ||knowsOverridePassword {2println("Welcome!")3}else{4println("ACCESS DENIED")5}6//prints "Welcome!" //输出"Welcome!"
括号将前两个值与其他值分隔开来,使其作为整体逻辑中的一种可选状态的意思更加明显。组合表达式的结果不会改变,但对读者而言,整体意图更加清晰。可读性总是优先于简洁性;应尽可能在合适的地方使用括号,使你的逻辑更加明晰。
谢谢,Swifter-QQ群:362232993,同好者进~
Fork:https://github.com/Joejo/Swift-lesson-for-chinese