第3章 万丈高楼平地起——基本操作符
操作符是任何计算机语言的最核心部分之一。例如,四则运算、求余、逻辑运算对于程序都是非常重要的。在Swift语言中,除了支持常规的操作符外(任何语言都支持的操作符),还新增加了很多操作符,以及对部分常规操作符进行了扩展(如求余支持浮点数)。本章将详细介绍 Swift 支持的最基本,也是最常用的操作符。更复杂的操作符会在后面的章节详细讨论。
本章要点
□ Swift语言支持哪些操作符
□ 赋值操作符
□ 数组操作符
□ 复合赋值操作符
□ 比较操作符
□ 三元条件操作符
□ 区间操作符
□ 逻辑操作符
3.1 操作符的种类
操作符(Operator,或称为运算符)是所有语言的必需品,Swift也不例外。通常操作符需要和操作数一同出现。根据操作数的个数,可以分为一元、二元和三元操作符。这里的“元”就是指操作数。
□ 一元操作符:只有一个操作数。例如,符号(−)、自加 (++)、自减(−−)都属于一元操作符。一元操作符又分为前置和后置一元操作符。这里的前置和后置是指操作符相对于操作数的位置。例如,-a中的“-”就是前置操作符,而a++中的“++”就属于后置操作符。
□ 二元操作符:这类操作符是最常用的,绝大多数操作符都属于这类操作符。例如,最常用的加(+)、减( −)、乘(*)、除(/)就属于二元操作符。二元操作符是中置的,也就是说,操作符会出现中两个操作数之间,例如a + b。
□ 三元操作符:这类操作符可能并不多见。在Swift语言中只有一个三元操作符,就是三元条件操作符:a?b:c。其中a、b、c是操作数,夹在它们之间(“?”和“:”)的是操作符。
3.2 赋值操作符
赋值操作(a = b)表示用b的值来初始化或更新a的值,例如,下面是一些标准的赋值操作。
var a = 10
let b = a
b = 10
a = b
如果等号(=)右侧是一个元组类型的值,那么等号左侧也必须是一个元组形式的变量或常量。而且在赋值的过程中,右侧元组值中每一个分量值会被分别赋给左侧对应的变量或常量。
var (x, y, z) = (1,2,3)
// 现在 x 等于 1, y 等于 2
要注意的是,在C语言和Objective-C中,需要Bool值的地方也可以使用整数值代替,0表示false,非0表示true。所以下面的代码在C或Objective-C中是正确的。
int x = 20
int y = 100
if(x = y) // 可以满足条件
{
... ...
}
对于 Swift语言来说,赋值语句是不返回值的,所以下面的代码在 Swift语言中是错误的。
var x = 20
var y = 100
if x = y
{
// 无法编译通过,因为x = y不会返回任何值
}
Swift的这个特性使你无法将等于操作符(==)错写成赋值操作符(=)。尽管像Java这样的语言可以在一定程度上避免这种错误(因为在Java中不能用整数代替Bool类型值),但如果赋值的变量正好是一个布尔类型,那么仍然可能错将(==)错写成(=)。
3.3 数值操作符
Swift 语言支持标准的四则运算操作符,其他数值操作符还包括整数求余、浮点数求余、自增、自减、一元负号、一元正号操作符。本节将介绍这些操作符的基本使用方法。
3.3.1 四则运算操作符
Swift 中所有数值类型都支持基本的四则运算。
□ 加法(+)。
□ 减法(−)。
□ 乘法(*)。
□ 除法(/)。
例如,下面是一些数值进行基本运算的例子。
letx= 1+ 2 // x= 3
var y= 5 -3 // y= 2
var abc= 2*3 // abc =6
var c= 10.0 /2.5 // c= 4.0
与 C 语言、Objective-C、Java等语言不同,Swift的数值默认是不允许溢出的。例如,下面的代码是无法编译通过的。
let x:Byte = 1234 // 1234超出了Byte的取值范围
var y:Byte = 12*33 // Swift编译器在编译时会计算12*33的值,很明显,它们的乘积超出了Byte的范围
如果在计算数值时使用的变量、常量或方法(函数)返回的值,即使数值超出范围,也是可以编译通过的,但执行时会抛出异常,也就是说溢出会变成一个运行时错误。例如,下面的代码在运行时程序会中断。
let x:Byte = 120
var y:Byte = 20 * x // 执行这条语句会抛出异常
如果需要考虑到溢出的情况,可以使用溢出操作符进行运算,例如,a &* b。即使 a和b的乘积超出了数值类型的取值范围,也会输出溢出的结果。由于Swift中的一些高级操作符涉及现在还没讲过的概念,所以这些高级操作符(包括溢出操作符)将放到本书的后面讲解。
加法(+)运算符不仅能用于数值的相加,也能用于字符和字符、字符和字符串以及字符串和字符串之间的连接。这3种情况下连接的结果都是字符串。
let c1:Character = "a"
let c2:Character = "b"
var s1:String = c1 + c2 // s1 = "ab"
var s2:String = c1 + s1 // s2 = "aab"
var s3:String = s1 + s2 // s3 = "abaab"
3.3.2 整数求余
整数求余(a%b)就是指计算a最多可以容纳多少个b,最后多出来那部分就是余数。例如,9 % 4 = 1,这里1就是余数。也就是说,9最多容纳2个4,那么剩余的那个1就是余数。
要注意的是,求余操作符(%)在其他语言中又可以称为取模运算。不过实际上,%可以对负数进行操作,所以“求余”比“取模”可能更合适些。
如果要为求余操作符给出一个计算公式的话,那么这个公式如下。
a = (b ×倍数) + 余数
这个公式也可以是。
余数 = a - (b ×倍数)
如果对负数使用%,那么计算结果也是负数。例如,−9 % 4 = −1,如果将这个表达式带入公式,结果就是。
-9 = ( 4 × -2) + -1
所以余数是−1。
注意
如果%后面的操作数(也就是b)为负数,那么这个负号将被忽略。也就是说 a % b = a % (-b)。另外,%后面的操作数如果加负号,要用圆括号括起来。
3.3.3 浮点数求余
与其他语言不同,Swift是可以对浮点数求余的。例如,8.5 % 2.5 = 1.0。浮点数求余也可以使用整数求余的公式。如果将这个表达式代入公式的结果是。
8.5 = (2.5 × 3) + 1.0
3.3.4 自增和自减
和C语言一样,Swift也提供了使变量增1和减1的操作符。自增使用两个加号(++),自减使用两个减号(−−)。不过和C语言不同的是操作数可以是整数,也可以是浮点数。
和C语言一样,Swift 也提供了方便对变量本身加1或减1的自增(++)和自减(−−)的运算符。其操作对象可以是整形和浮点型。
自增和自减还分为前自增、后自增;前自减、后自减。这里的前和后是指自增和自减操作符相对于操作数的位置。前自增和前自减操作符位于操作数前面,例如,++a、−−b。后自增和后自减操作符位于操作数后面,例如,a++、b−−。它们的区别是当操作符位于操作数前面时,先自加或自减,然后再返回变量值。当操作符位于操作数后面时,先返回变量值,然后再自加或自减。
var i = 1
var v = ++i // v = 2
var m = v++ // m = 2
var x = 2.5
x = ++x // x = 3.5
x = x++ // x = 3.5
尽管单独增加和减少变量值时使用++a、a++、−−a和a−−都可以。但推荐使用++a和−−a,因为在返回变量值之前先增1或减1更符合逻辑。
3.3.5 一元负号和正号
数值的正负号可以使用前缀−(即一元负号)来切换。
let three= 3
letminusThree= -three // minusThree等于 −3
letplusThree= -minusThree // plusThree等于 3
要注意的是一元负号(−)需要写在操作数之前,与操作数之间没有空格。
一元正号(+)不做任何改变地返回操作数的值。使用规则与一元负号相同。尽管一元正好做的是无用功,但当使用一元负号表示一个负数时,使用一元正号表示正数会使你的代码更容易阅读,当然,对于某些审美能力很强的人来说,会看出代码之美。
3.4 复合赋值操作符
复合赋值(Compound Assignment)就是指将其他操作符和赋值操作符组合在一起使用,例如+=、−=都属于复合赋值操作。基本上属于C风格的语言(Java、C#、C++等)都支持这类操作符。
我们也可以将复合赋值操作符看着是两个操作数进行运算并赋值的简写形式,例如,a += b相当于a = a + b。
var a = 1
a += 2 //相当于a = a + 2,所以a等于 3
注意
符合赋值运算没有返回值,也就是说,let b = a += 2 是错误的写法。
3.5 比较操作符
所有在标准C语言中的比较运算都可以在Swift中使用。
□ 等于(a == b)。
□ 不等于(a != b)。
□ 大于(a > b)。
□ 小于(a < b)。
□ 大于等于(a >= b)。
□ 小于等于(a <= b)。
注意
在 Swift 语言中还支持恒等(3 个等号,===)和不恒等(一个感叹号和两个等号,!==)两个比较操作符。这两个操作符用来判断两个对象是否引用了同一个对象实例。更多关于这两个操作符的细节,将在后面介绍类和结构体时详细讨论。
每一个比较操作符都返回一个Bool类型的值,所以,比较操作符会经常用于if语句中,关于if语句的细节会在后面的章节中详细讨论。
下面是一些使用比较操作符的例子。
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
let name = "bill"
if name == "world" // 此处条件为false
{
println("yes")
}
else // 此处条件为true
{
println("no")
}
3.6 三元条件操作符
Swift中只有一个三元操作符。操作符的原型如下。
逻辑表达式?为true时的答案:为false时的答案
其实三元条件操作符和前面介绍的复合赋值操作符一样,也是为了简化表达式而存在的,所以并不是必须的,但使用该操作符却能提升程序的开发效率,使程序看起来更美观。
三元条件操作符实际上是简化了下面的代码。
if 逻辑表达式
{
为true时的答案
}
else
{
为false时的答案
}
下面是一个实际的例子,在该例子中分别演示了不使用三元逻辑操作符和使用三元逻辑操作符的情况。
var value:Int
var flag:Bool = true
// 不使用三元逻辑操作符的情况
if flag
{
value = 1
}
else
{
value = 2
}
// 使用三元逻辑操作符的情况
value = (flag ? 1 : 2)
3.7 区间操作符
区间操作符可以非常方便地确定一个区间,通常是一个数值的区间。并且可以枚举这个区间中的每一个值。尽管区间操作符用起来很方便,但并不是所有的语言都支持区间操作符,对于静态语言来说,Pascal 是支持区间操作符的。不过幸好 Swift 也支持区间操作符,这将会给我们编写代码带来很多方便。
区间操作符分为闭区间操作符和半开半闭操作符。闭区间操作符使用3个点(...)表示,该操作符包含了区间两端的值。而半开半闭操作符使用两个点加一个小于号(..<)表示 ,该操作符左侧端点的值会被包含,但不包含右侧端点的值。
区间操作符在使用for-in循环枚举数组或字典中元素时非常有用。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
// i会从0循环到count – 1
for i in 0..<count
{
println("第 \(i + 1) 个人叫 \(names[i])")
}
执行上面的代码,会输出如下的结果。
第 1 个人叫 Anna
第 2 个人叫 Alex
第 3 个人叫 Brian
第 4 个人叫 Jack
当然,我们还可以使用闭区间对数值进行循环。
for i in 1...5
{
print("index = \(i) ")
}
执行上面的代码,会输出如下的内容。
index = 1 index = 2 index = 3 index = 4 index = 5
注意
经测试发现,区间操作符是可以使用浮点数的,例如,1...1.2,不过使用浮点数会使Swift程序员不好处理。读者自己测试下就知道了,如果使用 for-in语句,会无限循环下去,每次都会输出很大的数。这可能是因为Swift最开始确定的开发理念是尽可能满足更多的表达方法,估计是能用浮点数的都可以使用浮点数了(如求余)。但可能还没考虑全(编译器未实现完整),所以在对区间操作符使用浮点数时,就会出现无法预料的结果,大家还是别用浮点数了。如果要真用浮点数,Swift最好用步长之类的东西。否则没法循环了。
3.8 逻辑操作符
逻辑运算的操作对象是逻辑布尔值。Swift支持基于C语言的3个标准逻辑运算。
□ 逻辑非(!a)。
□ 逻辑与(a && b)。
□ 逻辑或(a || b)。
3.8.1 逻辑非
逻辑非运算(!a)对一个布尔值取反,使得true变为false,或使false变为true。
逻辑非是一个前置操作符,需出现在操作数的前面,并且操作符和操作数之间不能加空格。读作“非 a”或“对a取反”,现在来看下面的例子。
let allowed = false
if !allowed
{
println("allowed")
}
else
{
println("denied");
}
执行这段代码后,会输出“allowed”。
3.8.2 逻辑与
逻辑与(a && b)表达了只有a和b的值都为true时,整个表达式的值才会是true。只要任意一个值为false,整个表达式的值就为false。事实上,如果第一个值为false,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做“短路计算(short-circuit evaluation)”。
在下面的例子中,只有两个Bool值都为true值的时候才会执行if语句中的代码,否则会执行else中的代码。
let entered = true
let passed = false
if entered && passed
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
在执行上面的代码后,会输出“ACCESS DENIED”
3.8.3 逻辑或
逻辑或(a || b)是一个由两个连续的“|”组成的中置运算符。它表示了两个逻辑表达式的其中一个为true,整个表达式就为true。
同逻辑与运算类似,逻辑或也是“短路计算”的,当左端的表达式为true时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
在下面的代码中,第一个布尔值(hasKey)为 false,但第二个值(knowsPassword)为 true,所以整个表达是true,于是允许进入:
let hasKey = false
let knowsPassword = true
if hasKey || knowsPassword
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
执行这段代码后,会输出“Welcome!”。
3.8.4 组合逻辑
我们可以组合多个逻辑运算来表达一个复合逻辑。
if entered && passed || hasKey || knowsPassword
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
执行这段代码后,会输出“Welcome!”。
如果只是简单地将逻辑操作符组合在一起,那么系统会从左到右依次计算,例如,对于这段代码来说,会先计算“entered && passed”,然后用计算结果与hasKey进行逻辑或,最后再用逻辑或的结果与knowsPassword进行逻辑或。
3.8.5 使用圆括号指定优先级
对于一个复杂的表达式,可能需要指定先运算那一对操作数,这就要使用圆括号来确定优先级。例如,下面的表达式就会先将passed和hasKey进行逻辑或,然后再与entered逻辑与,最后与knowsPassword逻辑或。
if entered&&(passed || hasKey) || knowsPassword
{
println("Welcome!")
}
else
{
println("ACCESS DENIED")
}
执行这段代码后,会输出“Welcome!”。
如果逻辑表达式比较复杂,建议使用圆括号指定优先级,这样不仅不易出错,而且也增加了程序的可读性。
3.9 小结
尽管 Swift 语言支持的大多数操作符和其他语言类似,但对于刚入门的程序员来说,最好仔细阅读本章的内容。因为,如果对这些基本的操作符不了解或不能熟练使用的话,会在以后的学习和工作中遇到很大的困难。当然,对于已有多年工作经验的读者来说,只需要看一下Swift特有的操作符(如区间操作符)即可,其他内容可直接跳过。