本文将介绍 Swift 语言的循环执行方式
# 01、循环执行方式
在Swift语言中,主要有两种循环执行控制方式: for-in结构和while结构。while结构又细分为当型while结构和直到型while结构,后者称为repeat-while结构。下面首先介绍for-in结构。
循环控制方式for-in结构可用于区间中的整数值遍历、字符串中的字符遍历、字典中的元素遍历等,常用的形式如下。
(1) 典型for-in结构。
上述for-in结构中,遍历“范围”中的各个“元素”,对于每个“元素”执行“语句组”一次。这里无须为“元素”定义变量类型,自动根据“范围”中的数组类型设定“元素”的数据类型。
如果“元素”使用“_”替换,则表示不关注“范围”中的具体元素,只关注循环次数。表示“范围”的方法可借助运算符“...”和“..<”,或者使用函数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。
for-in结构中的“范围”可以为字符串或字典。
(2) 用于字典遍历的for-in结构。
在上述for-in结构中,使用“二元元组”作为字典中每个元素的返回值,例如,将二元元组表示为“(k,v)”,则k将对应着字典元素的键值,v将对应着同一个字典元素的值。如果k用“”表示,则将只关心字典元素的值,而不关心它的键值。如果v用“”表示,则将只关心字典元素的键值,而不关心它的值。
程序段1介绍了for-in结构的用法。
程序段1for-in结构的用法实例
程序段1的执行结果如图1所示。
■ 图3-5程序段3-11的执行结果
下面结合图3-5介绍程序段3-11的执行过程。在程序段3-11中,第2行“var s=1”定义整型变量s,赋初值为1。第3~6行为一个for-in结构,第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行为一个for-in结构,第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行为一个for-in结构,第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的作用范围为for-in结构,故在其他的for-in结构中仍然可以使用同名的局部变量e。
第23~32行实现了与第16~21行相同的功能,这里为演示continue语句的用法。第23行“s=0”将0赋给变量s。第24~31行为一个for-in结构,第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”,表示跳过for-in结构中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语句,跳出它所在的for-in结构,这里跳转到第47行执行。
现在总结一下continue和break的特点: ①continue和break均可以用于if、switch、for-in和while结构中; ②在循环体中遇到continue,将跳过循环体内continue之后的语句,转到循环开头执行条件判断,开始下一次循环; ③在循环体内遇到break语句,将跳出该循环体,转到循环体外的下一条语句执行; ④对于嵌套的循环体而言,continue和break语句仅对它所在的循环体有效,当有多个嵌套的循环体时,为了明确continue和break作用的循环体,可以为它们所在的循环体开头添加标号,指示continue和break的跳转,这个仅是为了方便程序阅读,第49~62行中演示了这种用法。
第49~62行实现了与第16~21行相同的功能。第50~61行为一个for-in结构,第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行为一个for-in结构,这里实现了凯撒密码,即一个字符用其后的第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行为一个for-in结构,第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行为一个for-in结构,第81行“for(_,v) in dic”中,只关注从字典dic中遍历到的元素的值,将这个值赋给v,对于每次遍历,执行第83行“t+=v”,这里表示计算字典中所有水果的单价和。第85行“print("Total price:",t)”输出字符串“Total price:”和t的值。
上述介绍的for-in结构的特点在于循环变量的取值范围是确定的,可以准确地推断出循环的次数。而当循环次数不可知时,一般不使用for-in结构,而使用while循环控制结构。while结构分为标准while结构和repeat-while结构两种,其语法如下。
(1) 标准while结构,即当型while结构。
在上述结构中,当“条件表达式”为真时,循环执行“条件表达式为真时执行的语句组”,直到“条件表达式”为假时跳出while循环体。在“条件表达式为真时执行的语句组”中,一般包含有调整“条件表达式”结果的语句。在标准while结构中,如果“条件表达式”为假,则“条件表达式为真时执行的语句组”可能一次也得不到执行。
(2) repeat-while结构,即直到型while结构。
在上述结构中,先执行一次“语句组”,再判断“条件表达式”的值,若其为真,则循环执行“语句组”,直到“条件表达式”为假,跳出repeat-while循环体。在repeat-while结构中,表示循环体的“语句组”至少可以被执行一次。
程序段2介绍了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多年前的算法是求两数的最大公约数最快的方法。
程序段 2 while 结构用法实例
程序段2的执行结果如图2所示。
■ 图2 程序段2的执行结果
下面结合图2介绍程序段2的执行过程。在程序段3-12中,第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”若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行相同的功能,这里使用了repeat-while结构。第21行“(a,b)=(a0,b0)”将a0赋给a,将b0赋给b。第22~27行为一个repeat-while结构,循环执行第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的最小公倍数。