第3章 运算符与程序控制 Swift语言具有众多的运算符,根据运算符作用的操作数的个数,可将运算符分为单目运算符、双目运算符和三目运算符。根据运算符的性质,又可分为算术运算符、关系运算符、逻辑运算符、位运算符和赋值运算符等。运算符和操作数构成表达式,或称语句,是程序执行的基本单元。程序执行主要有三种控制方式,即顺序执行、分支执行(或称选择执行)和循环执行,当考虑硬件触发中断或软件异常处理时,还有一种中断执行方式。本章将介绍Swift语言的运算符和主要程序控制方式。 表31为按优先级排列的全部运算符。注意,在语句中,加圆括号“()”部分运算优先级更高。 表31Swift语言运算符 优 先 级 别运算符含义 1 (最高) <<按位左移 >>按位右移 &<<按位左移(忽略溢出) &>>按位右移(忽略溢出) 2 *乘法 &*乘法(带溢出) /除法 %取余 &按位与 3 +加法 &+加法(带溢出) -减法 &-减法(带溢出) |按位或 ^按位异或 4 ..<左闭右开的半开区间 ....闭区间 5 is判断一个对象是否属于某个子类实例 as将子类对象用作父类实例 as!将子类对象强制用作父类实例 as?将子类对象用作父类实例(失败返回nil) 6??表达式为nil时返回其后的默认值 7 <小于 <=小于或等于 >大于 >=大于或等于 ==等于 !=不等于 ===两个对象为同一个实例 !==两个对象不为同一个实例 ~=模式匹配 .<向量逐点小于 .<=向量逐点小于或等于 .>向量逐点大于 .>=向量逐点大于或等于 .==向量逐点等于 .!=向量逐点不等于 8 &&逻辑与 .&向量逐点按位与 9 ||逻辑或 .|向量逐点按位或 .^向量逐点按位异或 10?:三目运算符(或称条件运算符) 11 (最低) =赋值 *=相乘与赋值复合 /=除法与赋值复合 %=取余与赋值复合 +=加法与赋值复合 -=减法与赋值复合 <<=按位左移与赋值复合 >>=按位右移与赋值复合 &=按位与与赋值复合 |=按位或与赋值复合 ^=按位异或与赋值复合 &*=带溢出乘法与赋值复合 &+=带溢出加法与赋值复合 &-=带溢出减法与赋值复合 &<<=忽略溢出按位左移与赋值复合 &>>=忽略溢出按位右移与赋值复合 .&=向量逐点按位与与赋值复合 .|=向量逐点按位或与赋值复合 .^=向量逐点按位异或与赋值复合 表31中的运算符处于操作数中间,称这类运算符为中缀运算符。除表31中的中缀运算符外,还有7个可用作前缀的运算符和1个可用作后缀的运算,如表32所示。 表32Swift语言的前缀和后缀运算符(设a为操作数) 运算符性质运算符含义 前缀运算符 !逻辑非 .!向量逐点逻辑非 ~按位取反 +正 -负 ..<“..<a”表示从首位置至a的前一个位置的范围 ...“...a”表示从首位置至a表示的位置的范围 后缀运算符...“a...”表示从a表示的位置至最后一个位置的范围 注意区分表31和表32中表示区间的运算符“...”和“..<”。除了 Swift标准库运算符外,用户可以自定义运算符,默认自定义运算符的优先级属于DefaultPrecedence,略高于三目运算符; 可以为自定义运算符指定优先级,表31中的各个优先级号对应的优先级组名如表33所示。 表33优先级号与优先级组名对应关系表 优先级号优先级组名优先级号优先级组名 1BitwiseShiftPrecedence7ComparisonPrecedence 2MultiplicationPrecedence8LogicalConjunctionPrecedence 3AdditionPrecedence9LogicalDisjunctionPrecedence 4RangeFormationPrecedence自定义DefaultPrecedence 5CastingPrecedence10TernaryPrecedence 6NilCoalescingPrecedence11AssignmentPrecedence 下面将介绍常用的运算符。 3.1算术运算符 算术运算符包括加法“+”、减法“-”、乘法“*”、除法“/”和取余“%”等,其中,对于加法、减法和乘法操作的运算符在计算过程中将自动进行越界管理(对于除法和取余不会溢出),当发生溢出时报错。可容许溢出的加法“&+”、减法“&-”和乘法“&*”运算符将自动舍弃计算中溢出的部分。加法、减法、乘法和除法运算符可适用于整型和浮点型,而取余运算只能适用于整型数。注意,加法“+”还可用于字符串连接操作。 下面以整型数为例,介绍算术运算符的用法,如程序段31所示。 视频讲解 程序段31算术运算符的用法 1import Foundation 2 3let arr1 = Array(repeating: 22, count: 5) 4let arr2 = [Int](6...10) 5var arr3 : [Int] = [] 6print("arr1 = \(arr1)") 7print("arr2 = \(arr2)") 8arr3.append(arr1[0] * arr2[0]) 9arr3.append(arr1[1] / arr2[1]) 10arr3.append(arr1[2] + arr2[2]) 11arr3.append(arr1[3] - arr2[3]) 12arr3.append(arr1[4] % arr2[4]) 13print("arr3 = \(arr3)") 14let a : Int8 = 100 15let b : Int8 = 120 16let c : Int8 = a &* b 17print(c & 0x7F) 18print(Int(a) * Int(b)& 0x7F) 图31程序段31的执行结果 程序段31的执行结果如图31所示。 现在结合图31分析程序段31的执行过程。在程序段31中,第3行“let arr1=Array(repeating:22, count:5)”定义数组常量arr1,具有5个相同的元素22。第4行“let arr2=[Int](6...10)”定义数组常量arr2,为“[6, 7, 8, 9, 10]”。第5行“var arr3:[Int]=[]”定义数组变量arr3,为空数组。 第6行“print("arr1=\(arr1)")”输出数组arr1,得到“arr1=[22, 22, 22, 22, 22]”。第7行“print("arr2=\(arr2)")”输出数组arr2,得到“arr2=[6, 7, 8, 9, 10]”。 第8行“arr3.append(arr1[0]*arr2[0])”将arr1[0]与arr2[0]相乘,其积添加到arr3中。第9行“arr3.append(arr1[1]/arr2[1])”将arr1[1]除以arr2[1]的商添加到arr3中。第10行“arr3.append(arr1[2]+arr2[2])”将arr1[2]与arr2[2]的和添加到arr3中。第11行“arr3.append(arr1[3]-arr2[3])”将arr1[3]与arr2[3]的差添加到arr3中。第12行“arr3.append(arr1[4]%arr2[4])”将arr1[4]除以arr2[4]的余数添加到arr3中。第13行“print("arr3=\(arr3)")”输出数组arr3,得到“arr3=[132, 3, 30, 13, 2]”。 第14行“let a:Int8=100”定义常量a为有符号8位整数,赋值为100; 第15行“let b:Int8=120”定义常量b为有符号8位整数,赋值为120。第16行“let c:Int8=a &* b”定义常量c为有符号8位整数(范围为-128~127),赋值为a与b的容许溢出的乘法,这里,由于操作数均为有符号8位整数,故乘法将自动舍弃第7位以上的位数(注: 最低位为第0位),且将第7位视为符号位。第16行中如果使用“*”,将报溢出错误。 第17行“print(c & 0x7F)”将c与0x7F按位与,即将c的最高位清零,得到96。第16~17行相当于第18行“print(Int(a) * Int(b)& 0x7F)”,即将a和b转换为整数,作普通乘法,再与0x7F按位与,得到96。 最后,再强调一下取余操作。在Swift语言中,求整数a除以整数b的余数c,使用表达式c=a%b。这个运算与C++语言中的取余操作有区别,这里相当于求a中最多包含的b的个数n,然后,c=a-n*b。例如,30%7=30%(-7)=2,-30%7=-30%(-7)=-2,这是因为30=4*7+2=(-4)*(-7)+2,-30=(-4)*7+(-2)=4*(-7)+(-2)。可以将 “c=a%b”理解为“c=a-|a|%|b|*sign(a*b)*b”,其中,“|a|”表示a的绝对值,“sign(a*b)”表示a与b的乘积的符号。 3.2关系运算符和条件运算符 关系运算符包括小于“<”、小于或等于“<=”、大于“>”、大于或等于“>=”、等于“==”和不等于“!=”等。关系运算符连接操作数得到关系表达式,关系表达式的返回值为布尔型量(true或false)。 条件运算符为“?:”,是Swift语言中唯一的三目运算符,对于形式“a?b:c”而言,若a为真,则返回表达式b的结果,否则返回表达式c的结果。这里a必须为布尔量或返回值为布尔量的关系表达式。 下面借助程序段32介绍关系运算符和条件运算符的用法。 视频讲解 程序段32关系运算符和条件运算符实例 1import Foundation 2 3let str1 = "Hello Swift." 4let str2 = "Hello World." 5let n1 = 15, n2 = 18, d1 = 3.4, d2 = 5.6 6print(str1 > str2) 7print(str1 >= str2) 8print(str1 < str2) 9print(str1 <= str2) 10print(n1 == n2) 11print(n1 != n2) 12print(d1>d2 ? d1 : d2) 在程序段32中,第3行“let str1="Hello Swift."”定义字符串常量str1,赋值为“Hello Swift.”。第4行“let str2="Hello World."”定义字符串常量str2,赋值为“Hello World.”。第5行“let n1=15, n2=18, d1=3.4, d2=5.6”定义两个整型常量n1和n2以及两个双精度浮点型常量d1和d2,依次赋值为15、18、3.4和5.6。 第6行“print(str1 > str2)”输出关系表达式“str1>str2”的结果,由于“S”<“W”,故“str1<str2”,这里输出false。第7行“print(str1 >= str2)”输出关系表达式“str1>=str2”的结果,这里输出false。第8行“print(str1 < str2)”输出关系表达式“str1<str2”的结果,得到true。第9行“print(str1 <= str2)”输出关系表达式“str1<=str2”的结果,得到true。 第10行“print(n1==n2)”输出关系表达式“n1==n2”的结果,得到false。第11行“print(n1!=n2)”输出关系表达式“n1!=n2”的结果,得到true。 第12行“print(d1>d2 ? d1:d2)”中,“d1>d2 ? d1:d2”借助条件运算符计算d1和d2中的最大者,这里输出5.6。 最后,需要补充说明两点: ①关系运算符不能级联,例如,类似于“3<4<5”这种用法是错误的; ②条件运算符可以嵌套,例如,形如“5> 3 ? (7>8 ? 10:12): 13”这种用法是正确的。 3.3逻辑运算符 在Swift语言中,逻辑常量仅有2个,即true与false。逻辑运算符只能连接逻辑量,并得到一个新的逻辑量。常用的逻辑运算符为逻辑非“!”、逻辑与“&&”和逻辑或“||”,优先级从高至低依次为逻辑非>逻辑与>逻辑或。逻辑运算符的运算规则如表34所示。 表34逻辑运算符的运算规则 逻辑运算符运 算 规 则 逻辑非“!”!false=true, !true=false 逻辑与“&&”false && false=false && true=true && false=false, true && true=true 逻辑或“||”false || true=true || false=true || true=true, false || false=false 逻辑表达式主要用于条件控制,在Swift语言中,表示“控制条件”的表达式结果只能为逻辑量。程序段33展示了逻辑运算符的用法。 视频讲解 程序段33逻辑运算符用法实例 1import Foundation 2 3var a1,a2 : Int 4a1 = -12 5a2 = 7 6var b1, b2 : Bool 7b1 = a1 >= a2 8b2 = a1 < a2 9print("b1=\(b1),!b1=\(!b1),b2=\(b2),!b2=\(!b2)") 10print("b1 && b2 = \(b1 && b2), b1 || b2 = \(b1 || b2)") 11if b1 12{ 13print("\(a1)>=\(a2)") 14} 15if b2 16{ 17print("\(a1)<\(a2)") 18} 图32程序段33的执行结果 程序段33的执行结果如图32所示。 现在,结合图32分析程序段33的执行情况。在程序段33中,第3行“var a1,a2:Int”定义两个整型变量a1和a2(默认均被初始化为0)。第4行“a1=-12”将-12赋给a1,注意,负号前有一个空格,在Swift语言中,尽可能在独立的语言元素(如变量、常量和运算符)间插入一个空格。第5行“a2=7”将7赋给变量a2。 第6行“var b1, b2:Bool”定义两个布尔型(即逻辑型)变量b1和b2,默认初始化值均为false。第7行“b1=a1 >= a2”将关系表达式“a1 >= a2”的结果赋给b1,这里为false。第8行“b2=a1 < a2”将关系表达式“a1 < a2”的结果赋给b2,这里为true。 第9行“print("b1=\(b1),!b1=\(!b1),b2=\(b2),!b2=\(!b2)")”输出b1、b1的逻辑非、b2和b2的逻辑非,将得到“b1=false, !b1=true, b2=true, !b2=false”。第10行“print("b1 && b2=\(b1 && b2), b1 || b2=\(b1 || b2)")”输出b1与b2的逻辑与和逻辑或,得到“b1 && b2=false, b1 || b2=true”。 第11~14行为一个if结构,由于b1为假,第12~14行不执行。第15~18行也为一个if结构,由于b2为真,第16~18行被执行,其中第17行“print("\(a1)<\(a2)")”输出“-21<7”。 注意: 不能把布尔量作为整型量使用,一般借助条件运算符将布尔量转换为整型,例如,设a为布尔量,则“a ? 1:0”返回1(若a为真)或0(若a为假)。 3.4位运算符与区间运算符 位运算符只能适用于整型,一般地,位运算符主要用于无符号整型。常用的位运算符有按位取反“~”、按位左移“<<”、按位右移“>>”、按位左移(忽略溢出)“&<<”、按位右移(忽略溢出)“&>>”、按位与“&”、按位或“|”和按位异或“^”等。其中,“按位取反”优先级最高; “按位左移”“按位右移”“按位左移(忽略溢出)”和“按位右移(忽略溢出)”的优先优相同,低于“按位取反”运算符; 然后,“按位与”运算符的优先级更低; 最后,“按位或”与“按位异或”优先级相同,它们的优先级最低。 位运算符直接作用于操作数的各位,设a和b为无符号8位整型,不妨设a为23,b为145,则表35给出了位运算符的计算情况。 表35位运算符计算结果(设a=23(0b00010111),b=145(0b10010001)) 位运算计 算 结 果位运算计 算 结 果 ~a0b11101000a& b0b00010001 ~b0b01101110a | b0b10010111 a>>20b00000101a ^ b0b10000110 a<<20b01011100(a&(1<<2)) >>21 a>>90b00000000a | (1<<6)0b01010111 a&>>90b00001011a ^ (1<<5)0b00110111 b>>20b00100100a& ~(1<<2)0b00010011 b<<20b01000100a& 0x0F0b00000111 位运算有一些特殊的用法,例如,右移一位相当于整数值除以2; 左移一位相当于整数值乘以2; 将一个整数a的第n位设为1而其余位不变,可用“a | (1<<n)”; 将整数a的第n位设为0而其余位不变,可用“a & ~(1<<n)”; 将整数a的第n位取反而其余位不变,可以用“a ^ (1<<n)”。 区间运算符只有两种表示: “...”和“..<”。设a和b为索引,则区间运算符可有五种情况: ①“a...b”表示从a至b的闭区间,包含两个端点; ②“a...”表示从a至最后一个索引号的闭区间,包括两个端点; ③“...b”表示从首索引至b的闭区间,包括两个端点; ④“a..<b”表示从a至b的半开区间,包括a但不包括b; ⑤“..<b”表示从首索引至b的半开区间,包括首位置,但不包括b端点。 程序段34展示了位运算符和区间运算符的用法。 视频讲解 程序段34位运算符与区间运算符用法实例 1import Foundation 2 3let a : UInt8 = 23 4let b : UInt8 = 145 5let sa = String(a, radix: 2) 6print("a: 0b" + String(repeating: "0", count: 8-sa.count)+sa) 7let sb = String(b, radix: 2) 8print("b: 0b" + String(repeating: "0", count: 8-sb.count)+sb) 9var res = [~a,~b,a >> 2,a << 2, a >> 9, a &>> 9, b >> 2, b << 2, a & b, a | b, a ^ b] 10 11for (i,e) in res.enumerated() 12{ 13print("res["+String(format: "%2d", i)+"]: 0b" + String(repeating: "0", count: 8-String(e,radix: 2).count)+String(e,radix: 2)) 14} 15 16var c1,c2,c3,c4: UInt8 17c1 = (a &(1<<2)) >> 2 18c2 = a | (1<<6) 19c3 = a ^ (1<<5) 20c4 = a & ~(1<<2) 21print("c1 = \(c1), c2 = \(c2), c3 = \(c3), c4 = \(c4)") 22print("c1: 0b" + String(repeating: "0", count: 8-String(c1,radix: 2).count)+String(c1,radix: 2)) 23print("c2: 0b" + String(repeating: "0", count: 8-String(c2,radix: 2).count)+String(c2,radix: 2)) 24print("c3: 0b" + String(repeating: "0", count: 8-String(c3,radix: 2).count)+String(c3,radix: 2)) 25print("c4: 0b" + String(repeating: "0", count: 8-String(c4,radix: 2).count)+String(c4,radix: 2)) 26 27let arr = Array(10...19) 28print("arr[3...5] = \(arr[3...5])") 29print("arr[3..<5] = \(arr[3..<5])") 30print("arr[3...]= \(arr[3...])") 31print("arr[...5]= \(arr[...5])") 32print("arr[..<5]) = \(arr[..<5])") 33let str = "Hello Swift." 34print("\(str[..<str.index(str.endIndex,offsetBy: -4)])") 程序段34的执行结果如图33所示。 图33程序段34的执行结果 下面结合图33介绍程序段34。在程序段34中,第3行“let a:UInt8=23”定义无符号8位整型常量a,赋值为23; 第4行“let b:UInt8=145”定义无符号8位整型常量b,赋值为145。第5行“let sa=String(a, radix:2)”将a转换为二进制数表示的字符串; 第6行“print("a:0b"+String(repeating:"0", count:8sa.count)+sa)”以8位长显示a的二进制数表示,输出“0b00010111”。第7行“let sb=String(b, radix:2)”将b转换为二进制数表示的字符串; 第8行“print("b:0b"+String(repeating:"0", count:8sb.count)+sb)”以8位长显示b的二进制数表示,输出“0b10010001”。 第9行“var res=[~a,~b,a >> 2,a << 2, a >> 9, a &>> 9, b >> 2, b << 2, a & b, a | b, a ^ b]”定义数组res,其元素依次为a的按位取反值、b的按位取反值、a右移2位、a左移2位、a右移9位、a忽略溢出右移9位、b右移2位、b左移2位、a与b、a或b、a异或b。第11~14行为一个forin循环结构,用于输出数组res。这里,“res.enumerated()”将数组的每个元素序号和元素值打包成一个元组返回,第11行“for (i,e) in res.enumerated()”中i为元素序号(即下标值),e为i对应的元素值。第13行“print("res["+String(format:"%2d", i)+"]:0b"+String(repeating:"0", count:8String(e,radix:2).count)+String(e,radix:2))”格式化输出i和e的值,例如,对于res[0]得到“res[ 0]:0b11101000”。表35和图33中列出了数组res的所有结果。 第16行“var c1,c2,c3,c4:UInt8”定义无符号8位整型变量c1、c2、c3和c4。第17行“c1=(a &(1<<2)) >> 2”计算a的第2位(注: 从右向左从第0位算起)上的值,赋给c1。第18行“c2=a | (1<<6)”将a的第6位置为1,然后,赋给c2。第19行“c3=a ^ (1<<5)”将a的第5位取反后赋给c3。第20行“c4=a & ~(1<<2)”将a的第2位清零后赋给c4。第21行“print("c1=\(c1), c2=\(c2), c3=\(c3), c4=\(c4)")”以十进制数格式输出c1、c2、c3和c4的值,得到“c1=1, c2=87, c3=55, c4=19”。第22~25行以二进制形式输出c1、c2、c3和c4的值,如图33所示。 第27行“let arr=Array(10...19)”定义常量数组arr,包含10~19共10个整数。第28行“print("arr[3...5]=\(arr[3...5])")”输出arr的第3~5个元素,得到“arr[3…5]=[13, 14, 15]”。第29行“print("arr[3..<5]=\(arr[3..<5])")”输出arr的第3~4个元素,得到“arr[3..<5]=[13, 14]”。第30行“print("arr[3...]= \(arr[3...])")”输出arr的第3个至最后一个元素,得到“arr[3...]=[13, 14, 15, 16, 17, 18, 19]”。第31行“print("arr[...5]=\(arr[...5])")”输出arr的首个至第5个元素,得到“arr[...5]=[10, 11, 12, 13, 14, 15]”。第32行“print("arr[..<5]= \(arr[..<5])")”输出arr的首个至第4个元素,得到“arr[..<5]=[10, 11, 12, 13, 14]”。 第33行“let str="Hello Swift."”定义字符串常量为“Hello Swift.”。第34行“print(" \(str[..<str.index(str.endIndex,offsetBy:-4)])")”输出字符串str的首个字符至倒数第5个位置间的子串,得到“Hello Sw”。 3.5赋值和复合赋值运算符 在Swift语言中,赋值运算符为“=”。赋值运算符可以和算术运算符或位运算符复合使用,例如,a=a + b可以简写为a += b,后者的“+=”为复合赋值运算符,这两个表达式都表示将a与b作加法运算,其结果赋给a。常用的复合赋值运算符有“*=”“&*=”“/=”“%=”“+=”“&+=”“-=”“&-=”“<<=”“&<<=”“>>=”“&>>=”“&=”“|=”“^=”等。 赋值和复合赋值运算符的优先级是所有运算符中级别最低的。注意区分赋值运算符“=”与关系运算符中的等于运算符“==”,赋值运算符将其右边表达式的计算结果赋给左边的变量或常量(注: 常量只能在定义时赋值一次),等于运算符用于判断其两端的表达式的结果是否相等,整个表达式的判定值为逻辑值。 程序段35是赋值和复合赋值运算符的应用实例。 视频讲解 程序段35赋值和复合赋值运算符实例 1import Foundation 2 3var sum = 0 4for i in 1...100 5{ 6sum += i 7} 8print("1+2+...+100 = \(sum)") 在程序段35中,第3行“var sum=0”定义变量sum,赋值为0。第4~7行为一个forin结构,i从1遍历到100,执行第6行100次。第6行“sum += i”使用复合赋值运算符实现sum与i相加的和赋给sum。第8行“print("1+2+...+100=\(sum)")”输出“1+2+...+100=5050”。 3.6程序执行方式 程序主要有三种执行方式,即顺序执行、分支执行和循环执行。程序总的执行方式为顺序执行,在Swift语言中,程序入口为 main.swift,然后,按照程序指令的“先后”位置顺序执行。为了实现复杂的算法,Swift语言带有分支和循环控制语句,可以实现程序代码的有条件执行和循环执行。下面首先回顾程序的顺序执行方式。 3.6.1顺序执行方式 Swift语言中,程序会按照语句的位置先后关系顺序执行,这是程序的基本执行方式,如程序段36所示,这个程序段实现了求三角形面积和周长。 视频讲解 程序段36顺序执行程序实例 1import Foundation 2 3let triangle = (3.4, 2.6, 4.7) 4var area,peri : Double 5peri = triangle.0+triangle.1+triangle.2 6let s = peri/2.0 7area = sqrt(s*(s-triangle.0)*(s-triangle.1)*(s-triangle.2)) 8print("perimeter: \(peri), area: \(String(format: "%5.2f", area))") 在程序段36中,语句按先后顺序依次执行。第3行“let triangle=(3.4, 2.6, 4.7)”定义元组triangle,包括了三角形的三条边长。第4行“var area,peri:Double”定义变量area和peri,分别用于保存三角形的面积和周长。 第5行“peri=triangle.0+triangle.1+triangle.2”计算三角形的周长,赋给peri。第6行“let s=peri/2.0”定义常量s,保存周长的一半。第7行“area=sqrt(s*(striangle.0)*(striangle.1)*(striangle.2))”利用海伦公式计算三角形的面积,赋给area。 第8行“print("perimeter:\(peri), area:\(String(format:"%5.2f", area))")”输出三角形的周长和面积,得到“perimeter:10.7, area:4.32”。 3.6.2分支执行方式 分支执行方式,又称选择执行方式或有条件执行方式。Swift语言通过if结构、switch结构和guard结构实现分支执行。if结构和switch结构均可以嵌套(包括互相嵌套和多级嵌套)使用,例如,if结构中包含另一个if结构、if结构中包括一个switch结构、switch结构中包含一个if结构等。 if结构的基本形式有以下三种。 (1) 基本if结构。 if条件表达式 { 条件表达式为真时执行的语句或语句组 } 在上述基本if结构中,如果if后的“条件表达式”为真,则执行“{ }”括起来的语句; 如果“条件表达式”为假,则跳至“}”后面的语句执行。 (2) 基本ifelse结构。 if条件表达式 { 条件表达式为真时执行的语句或语句组 } else { 条件表达式为假时执行的语句或语句组 } 在上述基本ifelse结构中,当条件表达式为真时,执行if下的“{ }”括住的语句; 否则,执行else下的“{ }”括住的语句。基本ifelse结构实现了两路选择。 (3) 多选择ifelse结构。 if条件表达式1 { 条件表达式1为真时执行的语句或语句组 } else if条件表达式2 { 条件表达式2为真时执行的语句或语句组 } ......//省略多个elseif块 else if条件表达式n { 条件表达式n为真时执行的语句或语句组 } else { 上述条件表达式1至n均为假时执行的语句或语句组 } 上述多选择ifelse结构中,else块可以省略。注意,if或 else部分不能为空,如果没有语句,则需要插入break。 下面举例说明if结构的用法,如程序段37所示,这个程序段实现了一元二次方程求根运算。 视频讲解 程序段37if结构用法实例 1import Foundation 2 3print("Please input a:") 4let a = Double(readLine() ?? "1.0") ?? 1.0 5print("Please input b:") 6let b = Double(readLine() ?? "2.0") ?? 2.0 7print("Please input c:") 8let c = Double(readLine() ?? "1.0") ?? 1.0 9var x1, x2 : Double 10if abs(a) < 1E-8 11{ 12print("Not a quadratic equation.") 13} 14else 15{ 16let delta = b*b - 4*a*c 17if delta > 1E-8 18{ 19x1 = (-b + sqrt(delta))/(2.0*a) 20x2 = (-b - sqrt(delta))/(2.0*a) 21print("x1 = \(String(format: "%5.2f", x1)), x2 = \(String(format: "%5.2f", x2))") 22} 23else if abs(delta) < 1E-8 24{ 25x1 = -b/(2.0*a) 26print("x1 = x2 = \(String(format: "%5.2f", x1))") 27} 28else 29{ 30x1 = -b/(2.0*a) 31x2 = sqrt(-delta)/(2.0*a) 32print("x1 = \(String(format: "%5.2f", x1)) + \(String(format: "%5.2f", x2))i", terminator: ", ") 33print("x2 = \(String(format: "%5.2f", x1)) - \(String(format: "%5.2f", x2))i") 34} 35} 在程序段37中,第3行“print("Please input a:")”输出提示信息“Please input a:”。第4行“let a=Double(readLine() ?? "1.0") ?? 1.0”从键盘读入浮点型数值,赋给a,如果输入非法,则a将赋值1.0。第5行print("Please input b:")”输出提示信息“Please input b:”。第6行“let b=Double(readLine() ?? "2.0") ?? 2.0”从键盘读入浮点型数值,赋给b,如果输入非法,则b将赋值2.0。第7行print("Please input c:")”输出提示信息“Please input c:”。第8行“let c=Double(readLine() ?? "1.0") ?? 1.0”从键盘读入浮点型数值,赋给c,如果输入非法,则c将赋值1.0。 第9行“var x1, x2:Double”定义双精度浮点型变量x1和x2,用于保存一元二次方程的两个根。第10~35行为一个ifelse结构,在else部分嵌套了一个ifelse结构。第10行“if abs(a) < 1E-8”表示如果a的绝对值小于10-8,则认为a近似为0,则执行第12行“print("Not a quadratic equation.")”输出“Not a quadratic equation.”; 否则,认为输入的a非0,则执行第15~35行。 第16行“let delta=b*b- 4*a*c”计算判别式delta的值。第17~34行为多选择的ifelse结构,分为三种情况。 (1) delta大于0,即第17行为真,这里用“delta > 1E-8”表示delta大于0。此时,一元二次方程具有两个不等的实根,第19行“x1=(-b + sqrt(delta))/(2.0*a)”计算第一个实根x1,第20行“x2=(-b-sqrt(delta))/(2.0*a)”计算第二个实根x2。第21行“print("x1=\(String(format:"%5.2f", x1)), x2=\(String(format:"%5.2f", x2))")”输出两个实根的值。 (2) delta等于0,即第23行的条件“abs(delta) < 1E-8”为真,这里用delta的绝对值小于10-8表示delta为0。此时,一元二次方程具有两个相同的实根,第25行“x1=-b/(2.0*a)”计算这个实根,第26行“print("x1=x2=\(String(format:"%5.2f", x1))")”输出两个相等的实根。 (3) 当上述两个情况都不满足时,即delta小于0时,执行第30~33行,此时一元二次方程具有一对共轭复根。第30行“x1=-b/(2.0*a)”计算根的实部,赋给x1; 第31行“x2=sqrt(-delta)/(2.0*a)”计算根的虚部,赋给x2。第32行“print("x1=\(String(format:"%5.2f", x1)) + \(String(format:"%5.2f", x2))i",terminator:", ")”输出第一个复根。第33行“print("x2=\(String(format:"%5.2f", x1)) - \(String(format:"%5.2f", x2))i")”输出第二个复根。 图34程序段37的执行结果 程序段37的执行结果如图34所示。 下面介绍switch结构的两种形式。 (1) 标准switch结构。 switch 表达式 { case 值1: 语句组1 case值2: 语句组2 case 值3: 语句组3 … case 值n: 语句组n default: 语句组n+1 } 在switch结构中,当“表达式”的值为“值1”时,执行“语句组1”,然后,跳出switch结构; 当“表达式”的值为“值2”时,执行“语句组2”,然后,跳出switch结构; 以此类推,当“表达式”的值为“值n”时,执行“语句组n”; 当“表达式”的值不为上述任一值时,执行“default”下的“语句组n+1”。注意,switch结构中的case部分必须涵盖所有的情况,如果不能包含所有情况,则必须添加“default”部分。 在switch结构中,若“表达式”的值为多个值,且执行同一个处理时,则在case中罗列这些值,并使用“ ,”分隔它们,形式如下所示: case值m, 值m+1, 值m+2, 值m+3: "表达式"为上述任一值时执行的语句组 此外,可以在case部分使用范围(或区间)运算符“..<”或“...”,例如: case0..<100: 当"表达式"的值为大于或等于0且小于100的整数时执行的语句组 (2) 具有“值绑定”和where子句的switch结构。 switch表达式 { case let x where 条件表达式1: 语句组1 case let y where条件表达式2: 语句组2 … case _: 语句组n+1 } 在上述switch结构中,当“条件表达式1”为真时,“表达式”的值赋给x,然后,执行“语句组1”,这里的“x”的作用域为“语句组1”,称为将x绑定在“语句组1”中。如果x的值在“语句组1”中可被改变,则使用“var x where条件表达式1”。当“条件表达式1”为假时,判断“条件表达式2”的值,若其为真,则将“表达式”的值赋给y,并执行“语句组2”,以此类推; 如果上述所有条件都不满足时,则“表达式”将匹配“_”(其中,“_”表示任意值),并执行“语句组n+1”。 在带有“值绑定”的switch结构中,where子句可根据情况使用,是可选项。一般使用元组作为switch的表达式,可实现多个值的匹配,下面以二元元组为例,介绍一种典型形式: switch (e1,e2)// e1和e2为元组的两个元素 { case let (x, 0): 当e2为0时执行的语句组,其中e1赋给x case let (x,y) where x>y: 将e1赋给x、e2赋给y,当x>y时执行的语句组 case let (x,y): 将e1赋给x、将e2赋给y,然后执行这时的语句组 } 在上述switch结构中,首先判断给定的元组“(e1, e2)”是否匹配形式“let(x, 0)”,即e2为0的情况(e1赋给x),如果匹配成功,则执行“当e2为0时执行的语句组,其中e1赋给x”。如果匹配不成功,则匹配“let (x,y) where x>y”,即“将e1赋给x、 将e2赋给y”且x大于y的情况,如果匹配成功,则执行“将e1赋给x、e2赋给y,当x>y时执行的语句组”。如果匹配不成功,则执行“case let (x,y)”部分,这种情况包含了其上述case匹配不成功的所有情况,此时将e1赋给x、将e2赋给y,执行“将e1赋给x、将e2赋给y,然后执行这时的语句组”,这里无须default部分。 在带有“值绑定”和where子句的switch结构中,case的“值”也可以组合使用,例如: switch 表达式 { case let x where x>10 && x<60, let x where x<-10 && x>-60: 语句组1 case _: 语句组2 } 在上述switch结构中,首先将“表达式”的值匹配case的条件“let x where x>10 && x<60, let x where x<-10 && x>-60”,即将“表达式”的值赋给x,如果x大于10且小于60,或者x小于-10且大于-60,则执行“语句组1”; 否则,执行“语句组2”。 注意: case部分不能为空,如果case部分没有语句,则需要插入break。 程序段38介绍了switch结构的用法。 视频讲解 程序38switch结构用法实例 1import Foundation 2 3let i = Int.random(in: 1...21) 4switch i 5{ 6case 1..<7: 7print(i,"is a small number.") 8case 7, 14,21: 9print(i,"is a big prize.") 10case let x where x % 2==0: 11print(x,"is an even number.") 12default: 13print(i,"is an odd number.") 14} 15 16let (j,k)=(Int.random(in: 1...100),Double.random(in: 0...1)) 17switch (j,k) 18{ 19case (let x, let y) where y>0.5,(let x,let y) where Double(x)*y>80: 20print("x=",x,", y=",String(format: "%4.2f", y),", y>0.5 or x*y>80") 21case (let x,let y) where x<50,(let x,let y) where Double(x)*y<20: 22print("x=",x,", y=",String(format: "%4.2f", y),", x<50 or x*y<20") 23case let (_,y): 24print("j=",j,", y=",String(format: "%4.2f", y)) 25} 在程序段38中,第3行“let i=Int.random(in:1...21)”生成一个1~21的伪随机整数,赋给常量i。第4~14行为一个switch结构,根据i的值做出选择: 首先,与第6行“case 1..<7”进行匹配,即判断i的值是否在1~7(不含后者)之间,若是,则执行第7行“print(i,"is a small number.")”输出i的值和字符串“is a small number.”,之后跳出switch结构,去执行第16行。如果第6行匹配失败,则与第8行“case 7, 14,21”相匹配,即判断i的值是否为7、14或21,如果是,则执行第9行“print(i,"is a big prize.")”输出i的值和字符串“is a big prize.”,之后跳至第16行执行。如果第8行匹配失败,则与第10行“case let x where x % 2==0”匹配,即将i的值赋给x,判断x是否为偶数,如果x为偶数,则执行第11行“print(x,"is an even number.")”输出x的值和字符串“is an even number.”。如果x不为偶数,则执行第12~ 13行,即default部分,输出i的值和字符串“is an odd number.”。 第16行“let (j,k)=(Int.random(in:1...100),Double.random(in:0...1))”定义一个二元元组,包含一个1~100的伪随机整数和一个0~1的伪随机双精度浮点数。第17~25行为一个switch结构,根据元组(j,k)的值做出选择,共有三种情况: ①第19行“case (let x, let y) where y>0.5,(let x,let y) where Double(x)*y>80”,表示将j的值赋给x,将k的值赋给y,这里包含了两个条件,即y大于0.5或者x与y的积大于80; ②第21行“case (let x,let y) where x<50,(let x,let y) where Double(x)*y<20”,表示将j的值赋给x,将k的值赋给y,这里也包含了两个条件,即x的值小于50或者x与y的积小于20; ③第23行“case let (_,y):”,这里不关注j的值,只是将k的值赋给y。第17行的switch语句依次与上述三种情况进行匹配,然后,执行匹配成功的情况下的语句,执行完后跳出switch结构。 注意: switch结构的各个case部分无须添加break,这一点和C语言不同; 如果某个case执行完后,希望其相邻的下一个case部分被直接执行(即无须判断这个case的条件部分),则在该case的执行语句后添加语句fallthrough。 除了if结构和switch结构外,还有一种分支结构,即guard结构。guard结构类似于if结构,但是guard结构必须有else部分,并且guard结构一般只用于函数中,保证某些条件为真,其典型结构如下: guard条件表达式 else { 语句组 return } 程序段39是继程序段38的部分代码,展示guard结构的用法。 视频讲解 程序段39guard结构用法实例 26func show(_ x: Int) 27{ 28guard x>=0 29else 30{ 31print("x is negative.") 32return 33} 34print(String(format:"x=%4d",x)) 35} 36show(10) 37show(-5) 在程序段39中,第26行“func show(_ x:Int)”定义了一个函数show,具有一个整型参数x,函数用法将在第4章中介绍。第27~35行为show函数体,第28~33行为一个guard结构,第28行“guard x>=0”判断x是否大于或等于0,如果为真,则执行guard结构之后的语句,即第34行语句; 如果为假,则执行第30~33行,输出字符串“x is negative.”,调用return跳出函数show。 第36行“show(10)”由于参数10为正数,故调用show函数输出“x=10”; 第37行“show(-5)”由于参数-5为负数,故调用show函数输出“x is negative.”。 除了上述分支控制结构外,还有continue和break语句可用于控制程序跳转,这两个语句不但可以用于if分支结构和switch分支结构,也可以用于while循环结构和for循环结构,故将这两个语句放在3.6.3节循环执行方式时介绍。此外,异常处理throw语句也可以实现程序跳转,将在第7章中介绍。 本节最后介绍一下借助if结构实现系统版本检查的方法。 在if结构中,可以通过available内置方法指定程序代码的最低运行平台,如程序段310所示。 视频讲解 程序段310设定程序代码运行的最低平台 1if #available(iOS 16.1.1,macOS 13.2.1, *) 2{ 3print("The program can be executed.") 4} 5else 6{ 7print("The program need to be updated.") 8} 9 10@available(macOS 13.2.1,*) 11struct student 12{ 13var name = "John" 14} 15if #available(macOS 13.2.1, *) 16{ 17let s = student() 18print(s.name) 19} 20else 21{ 22print("student cannot be used.") 23} 在程序段310中,第1行“if #available(iOS 16.1.1,macOS 13.2.1, *)”表示如果程序运行在iOS系统版本16.1.1及以上平台或者macOS系统版本13.2.1及以上平台时,执行第3行“print("The program can be executed.")”,输出信息“The program can be executed.”,否则(第5行“else”),执行第7行“print("The program need to be updated.")”,输出信息“The program need to be updated.”。第1行中的“*”表示没有其他的平台。在我们使用的MacBook笔记本电脑上,由于其系统为macOS 13.2.1,故执行第1~8行代码时,将输出信息“The program can be executed.”。 第10行“@available(macOS 13.2.1,*)”称为装饰器,也称“语法糖”,表示其下相邻的结构工作在macOS 13.2.1系统及其以上平台上,这里表示第11~14行的student结构工作在macOS 13.2.1系统及其以上平台上。第15~23行为一个if结构,第15行“if #available(macOS 13.2.1, *)”判断如果平台为macOS 13.2.1及其以上系统时,执行第17~18行,定义一个结构体常量s,输出s的成员name; 否则执行第22行“print("student cannot be used.")”输出提示信息“student cannot be used.”。 对程序做版本检查的好处在于使程序随着系统版本的升级而同步更新,保证程序使用最新的API(应用程序接口)函数。 3.6.3循环执行方式 在Swift语言中,主要有两种循环执行控制方式: forin结构和while结构。while结构又细分为当型while结构和直到型while结构,后者称为repeatwhile结构。下面首先介绍forin结构。 循环控制方式forin结构可用于区间中的整数值遍历、字符串中的字符遍历、字典中的元素遍历等,常用的形式如下。 (1) 典型forin结构。 for元素 in范围 { 语句组 } 上述forin结构中,遍历“范围”中的各个“元素”,对于每个“元素”执行“语句组”一次。这里无须为“元素”定义变量类型,自动根据“范围”中的数组类型设定“元素”的数据类型。 如果“元素”使用“_”替换,则表示不关注“范围”中的具体元素,只关注循环次数。表示“范围”的方法可借助运算符“...”和“..<”,或者使用函数stride(from:to:by:)或stride(from:through:by:)实现,两个stride的区别在于前者不包含“to”参数指定的边界,而后者包含“through”参数指定的边界。例如: stride(from:1, to:10, by:3)表示1~10、步长为3生成的序列,即1、4、7,不包含10; stride(from:1, through:10, by:3)表示1~10、步长为3生成的序列,即1、4、7、10,包含10。 forin结构中的“范围”可以为字符串或字典。 (2) 用于字典遍历的forin结构。 for 二元元组 in字典 { 语句组 } 在上述forin结构中,使用“二元元组”作为字典中每个元素的返回值,例如,将二元元组表示为“(k,v)”,则k将对应着字典元素的键值,v将对应着同一个字典元素的值。如果k用“_”表示,则将只关心字典元素的值,而不关心它的键值。如果v用“_”表示,则将只关心字典元素的键值,而不关心它的值。 程序段311介绍了forin结构的用法。 视频讲解 程序段311forin结构的用法实例 1import Foundation 2var s = 1 3for e in 1...10 4{ 5s *= e 6} 7print("10!=",s) 8 9s = 1 10for _ in 1...10 11{ 12s *= 2 13} 14print("2^10=",s) 15 16s = 0 17for e in stride(from: 1, to: 100, by: 2) 18{ 19s += e 20} 21print("1+3+...+99 = ",s) 22 23s = 0 24for e in stride(from: 1, through: 100, by: 1) 25{ 26if e % 2 == 0 27{ 28continue 29} 30s += e 31} 32print("1+3+...+99 = ",s) 33 34s = 0 35for e in 1...200 36{ 37if e % 2 == 0 38{ 39continue 40} 41if e>=100 42{ 43break 44} 45s += e 46} 47print("1+3+...+99 = ",s) 48 49s = 0 50mylabel: for e in 1...100 51{ 52if e % 2 == 0 53{ 54continue mylabel 55} 56if e >= 100 57{ 58break mylabel 59} 60s += e 61} 62print("1+3+...+99 = ",s) 63 64let p = "helloworld" 65var c : String = "" 66print("plain =",p) 67for e in p.unicodeScalars 68{ 69c += String(UnicodeScalar((e.value-97+4) % 26 + 97)!) 70} 71print("cipher=",c) 72 73let dic = ["Apple":3.5,"Pear":2.7,"Banana":5.2] 74for (k,v) in dic 75{ 76print(k,v,terminator: ", ") 77} 78print() 79 80var t = 0.0 81for (_,v) in dic 82{ 83t += v 84} 85print("Total price:",t) 图35程序段311的执行结果 程序段311的执行结果如图35所示。 下面结合图35介绍程序段311的执行过程。在程序段311中,第2行“var s=1”定义整型变量s,赋初值为1。第3~6行为一个forin结构,第3行“for e in 1...10”表示e在1~10的10个整数上遍历,即e依次取值1、2、……、10,循环执行第5行“s *= e”,这里为计算10的阶乘。第7行“print("10!=",s)”输出10的阶乘的值。 第9行“s=1”令变量s的值为1。第10~13行为一个forin结构,第10行“for _ in 1...10”中使用“_”作为循环变量,没有指定循环变量的名称,表示该循环只关注循环次数,这里循环10次,计算第12行“s *= 2”,即计算2的10次方。第14行“print("2^10=",s)”输出2的10次方的值。 第16行“s=0”将0赋给变量s。第17~20行为一个forin结构,第17行“for e in stride(from:1, to:100, by:2)”这里e遍历的序列为1、3、5、……、99,对于每个遍历值执行第19行“s+=e”,即计算100以内的正奇数的和。第21行“print("1+3+...+99=",s)”输出字符串“1+3+...+99=”和s的值。这里循环变量e的作用范围为forin结构,故在其他的forin结构中仍然可以使用同名的局部变量e。 第23~32行实现了与第16~21行相同的功能,这里为演示continue语句的用法。第23行“s=0”将0赋给变量s。第24~31行为一个forin结构,第24行“for e in stride(from:1, through:100, by:1)”表示循环变量e在范围1、2、……、100上遍历取值,对每个e,执行第26~30行,第26~29行为一个if结构,第26行“if e % 2 == 0”如果e为偶数,则执行第28行“continue”,表示跳过forin结构中continue后面的语句回到第24行执行条件判断,这里跳过第30行回到第24行,即忽略循环变量e为偶数的情况。第32行“print("1+3+...+99=",s)”输出字符串“1+3+...+99=”和s的值。 第34~47行实现了与第16~21行相同的功能,即计算100以内正奇数的和,这里为了演示break语句的用法。第41~44行为一个if结构,如果第41行“if e>=100”的条件为真,则执行第43行break语句,跳出它所在的forin结构,这里跳转到第47行执行。 现在总结一下continue和break的特点: ①continue和break均可以用于if、switch、forin和while结构中; ②在循环体中遇到continue,将跳过循环体内continue之后的语句,转到循环开头执行条件判断,开始下一次循环; ③在循环体内遇到break语句,将跳出该循环体,转到循环体外的下一条语句执行; ④对于嵌套的循环体而言,continue和break语句仅对它所在的循环体有效,当有多个嵌套的循环体时,为了明确continue和break作用的循环体,可以为它们所在的循环体开头添加标号,指示continue和break的跳转,这个仅是为了方便程序阅读,第49~62行中演示了这种用法。 第49~62行实现了与第16~21行相同的功能。第50~61行为一个forin结构,第50行“mylabel:for e in 1...100”在for循环体头部添加了标号mylabel,第54行continue mylabel表示continue执行时要跳转的标号为mylabel; 第58行break mylabel表示break要执行时跳出的循环体为mylabel指示的循环体。 第64行“let p="helloworld"”定义常量p,赋值为“helloworld”。第65行“var c:String=""”定义字符串变量c,赋值为空串。第66行“print("plain=",p)”输出字符串“plain=”和字符串p的值。第67~70行为一个forin结构,这里实现了凯撒密码,即一个字符用其后的第4个字符替换,第67行“for e in p.unicodeScalars”表示循环变量e在p的Unicode码形式的字符串中遍历各个字符,对于每个e,执行第69行“c+=String(UnicodeScalar((e.value-97+4)%26+97)!)”,这里“e.value”返回e的ASCII值,97为字符a的ASCII值,UnicodeScalar函数返回以整数值参数作为ASCII值对应的字符,这里将每个e变换后的字符转换为字符串添加到c的尾部。第71行“print("cipher=",c)”,输出加密后的密文文本c。第69行中的97可以使用“UnicodeScalar("a").value”替换,可避免去查字符a的ASCII值,这里,“UnicodeScalar("a").value”返回字符a的ASCII值。 第73行“let dic=["Apple":3.5,"Pear":2.7,"Banana":5.2]”定义字典dic。第74~77行为一个forin结构,第74行“for (k,v) in dic”中,元组(k,v)遍历dic字典的每个元素,其中,k存储遍历到的字典元素的键值,v存储遍历到的字典元素的值,对于每个元组(k,v),执行第76行“print(k,v,terminator:",")”,输出遍历到的键值对。第78行“print()”输出一个空行。 第80行“var t=0.0”定义双精度浮点型变量t,赋值为0.0。第81~84行为一个forin结构,第81行“for(_,v) in dic”中,只关注从字典dic中遍历到的元素的值,将这个值赋给v,对于每次遍历,执行第83行“t+=v”,这里表示计算字典中所有水果的单价和。第85行“print("Total price:",t)”输出字符串“Total price:”和t的值。 上述介绍的forin结构的特点在于循环变量的取值范围是确定的,可以准确地推断出循环的次数。而当循环次数不可知时,一般不使用forin结构,而使用while循环控制结构。while结构分为标准while结构和repeatwhile结构两种,其语法如下。 (1) 标准while结构,即当型while结构。 while条件表达式 { 条件表达式为真时执行的语句组 } 在上述结构中,当“条件表达式”为真时,循环执行“条件表达式为真时执行的语句组”,直到“条件表达式”为假时跳出while循环体。在“条件表达式为真时执行的语句组”中,一般包含有调整“条件表达式”结果的语句。在标准while结构中,如果“条件表达式”为假,则“条件表达式为真时执行的语句组”可能一次也得不到执行。 (2) repeatwhile结构,即直到型while结构。 repeat { 语句组 }while 条件表达式 在上述结构中,先执行一次“语句组”,再判断“条件表达式”的值,若其为真,则循环执行“语句组”,直到“条件表达式”为假,跳出repeatwhile循环体。在repeatwhile结构中,表示循环体的“语句组”至少可以被执行一次。 程序段312介绍了while结构的用法,实现了欧几里得求两个整数的最大公约数和最小公倍数的算法。欧几里得算法的基本原理: 设a和b为两个整数,不妨设a>b,则gcd(a,b)=gcd(b, a mod b),即a与b的最大公约数等于b与a模b的最大公约数,通过反复求模运算,最后可以表示为gcd(r,0),则r为a与b的最大公约数,最小公倍数为a*b/r。现在发现这个2000多年前的算法是求两数的最大公约数最快的方法。 视频讲解 程序段312while结构用法实例 1import Foundation 2 3var a0 = 6525 4var b0 = 81450 5var a,b : Int 6if a0<b0 7{ 8(a0,b0)=(b0,a0) 9} 10(a,b)=(a0,b0) 11var r = 1 12while r>0 13{ 14r = a % b 15a = b 16b = r 17} 18print("gcd(\(a0),\(b0))=\(a)") 19print("lcm(\(a0),\(b0))=\(a0*b0/a)") 20 21(a,b)=(a0,b0) 22repeat 23{ 24r = a % b 25a = b 26b = r 27}while(r>0) 28print("gcd(\(a0),\(b0))=\(a)") 29print("lcm(\(a0),\(b0))=\(a0*b0/a)") 30 31(a,b)=(a0,b0) 32while true 33{ 34r = a % b 35a = b 36b = r 37if r==0 38{ 39break 40} 41} 42print("gcd(\(a0),\(b0))=\(a)") 43print("lcm(\(a0),\(b0))=\(a0*b0/a)") 图36程序段312的执行结果 程序段312的执行结果如图36所示。 下面结合图36介绍程序段312的执行过程。在程序段312中,第3行“var a0=6525”定义整型变量a0,赋初值为6525。第4行“var b0=81450”定义整型变量b0,赋初值为81450。第5行“var a,b:Int”定义整型变量a和b。 第6~9行为一个if结构,第6行“if a0<b0”若a0小于b0,则执行第8行“(a0,b0)=(b0,a0)”将a0和b0的值对换。第10行“(a,b)=(a0,b0)”将a0赋给a,将b0赋给b。 第11行“var r=1”定义整型变量r,赋初值为1。第12~17行为一个while结构,实现欧几里得算法,第12行“while r>0”当r大于0时,循环执行第14~16行,这里的r保存a除以b的余数,如第14行“r=a % b”所示,然后,第15行“a=b”将b赋给a,第16行“b=r”将r赋给b,进行下一次循环,直到余数r为0。第18行“print("gcd(\(a0),\(b0))=\(a)")”输出a0和b0的最大公约数,第19行“print("lcm(\(a0),\(b0))=\(a0*b0/a)")”输出a0和b0的最小公倍数。 第21~29行实现了与第10~19行相同的功能,这里使用了repeatwhile结构。第21行“(a,b)=(a0,b0)”将a0赋给a,将b0赋给b。第22~27行为一个repeatwhile结构,循环执行第24~26行直到r小于或等于0。第28、29行依次输出a0和b0的最大公约数和最小公倍数。 第31~43行实现了与第10~19行相同的功能。这里使用了while结构,第31行“(a,b)=(a0,b0)”将a0赋给a,将b0赋给b; 第32行“while true”表示这是一个无限循环,循环执行第34~40行,在无限循环体中,添加了第37~40行的一个if结构,判断r的值是否为0(第37行“if r==0”),如果r为0,则执行第39行break,跳出while循环体,跳至第42行执行,第42行“print("gcd(\(a0),\(b0))=\(a)")”输出a0和b0的最大公约数; 第43行“print("lcm(\(a0),\(b0))=\(a0*b0/a)")”输出a0和b0的最小公倍数。 3.7本章小结 本章详细介绍了Swift语言的运算符,讨论了运算符的优先级以及常用运算符的用法,重点阐述了算术运算符、关系运算符、条件运算符、逻辑运算符、位运算符、区间运算符、赋值与复合赋值运算符等的用法。本章还详细介绍了程序流程控制方式,即顺序执行、分支执行和循环执行方式,重点讲述了if结构、ifelse结构、switch结构、forin结构和while结构等,借助本章的内容可以编写实现任意计算机数值算法的代码。 习题 1. Swift语言常用的运算符有哪些?讨论这些运算符的优先级情况。 2. 简述Swift语言的位运算符的用法。 3. 编程实现3n+1猜想,即输入一个整数,如果该整数为奇数,则将其乘以3加上1; 如果其为偶数,则将其除以2,如此循环下去,必能得到1。 4. 编程输入一个整数n,输出该整数的二进制形式。 5. 编程求一元二次方程ax2+bx+c=0的根。 6. 编程计算两个正整数的最大公约数和最小公倍数。 7. 输入一个正整数n,求它的素数因子。例如,n=80,则其素数因子为“2,2,2,2,5”。