[翻译]Swift编程语言——控制流
控制流for、while、if、switch和C基本一样 for循环for-in遍历一个范围、序列、集合中的每一个元素;for按照一个条件执行遍历,典型的就是在每次操作后计数加1 for-infor-in循环用来便利集合中的每一项。比如一定范围内的数字、数组中的每一项、字符串中的每一个字符等。 ?for? ?index? ?in? ?1?...?5? { ? ?println?(?"?(?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 上面例子中遍历的范围是1-5的闭合区间(使用了范围操作符…),index会被赋予初始值1(也就是范围的第一个数字),循环体内可以被引用执行。这个例子中,只有一条语句,就是对当前index的值乘5,输出结果字符串。当语句被执行后,index的值被范围内的第二个数字2替换,循环体内的语句被再次执行。就这样,一直到范围的最后一项。例子中的index是一个常量,它会在每次循环前被设置新值。index是被非显示声明的常量,在循环语句中简话了let。 ?let? ?base? = ?3 ?let? ?power? = ?10 ?var? ?answer? = ?1 ?for? ?_? ?in? ?1?...?power? { ? ?answer? *= ?base ?} ?println?(?"?(?base?)? to the power of ?(?power?)? is ?(?answer?)?"?) ?// prints "3 to the power of 10 is 59049" 上面例子是计算3的10次方。就是3连续乘自身10次(1-10的闭合区间)。循环体中不需要知道当前循环进行到的位置,这种情况下用下划线占位表示循环体内不需要引用当前循环值。 ?let? ?names? = [?"Anna"?,?"Alex"?,?"Brian"?,?"Jack"?] ?for? ?name? ?in? ?names? { ? ?println?(?"Hello,?(?name?)?!"?) ?} ?// Hello,Anna! ?// Hello,Alex! ?// Hello,Brian! ?// Hello,Jack! 你也可以通过键值对遍历字典。当字典在遍历时,会返回每一项对应的一个元组(形式为:(key,value)),可以在循环体内使用常量接收元组内容。下面的例子中,字典的键对应的常量名字叫做animalName,字典的值对应的常量名字叫做legCount。 ?let? ?numberOfLegs? = [?"spider"?: ?8?,?"ant"?: ?6?,?"cat"?: ?4?] ?for? (?animalName?,?legCount?) ?in? ?numberOfLegs? { ? ?println?(?"?(?animalName?)?s have ?(?legCount?)? legs"?) ?} ?// ants have 6 legs ?// cats have 4 legs ?// spiders have 8 legs 字典中的内容在遍历的时候可能和插入的顺序不同,因为字典天生就不是顺序存放内容的容器,不能保证插入顺序和遍历时的顺序一致。 for? ?character? ?in? ?"Hello"? { ? ?println?(?character?) ?} ?// H ?// e ?// l ?// l ?// o for一般的形式: for initialization; condition; increment { statements } 和C语言不同的就是缺少了圆括号。 initialization while condition { statements increment } 上面的格式中,常量和变量的定义在初始化语句中进行,变量和常量的作用范围只限于循环内部。如果你在循环结束后也使用定义的内容,那么你就需要在进入循环前就定义: var? ?index?: ?Int ?for? ?index? = ?0?; ?index? < ?3?; ++?index? { ? ?println?(?"index is ?(?index?)?"?) ?} ?// index is 0 ?// index is 1 ?// index is 2 ?println?(?"The loop statements were executed ?(?index?)? times"?) ?// prints "The loop statements were executed 3 times" 这里index的值是3,不是2。因为index是2执行完后会执行++index,index已经变成了3,index<3的条件不成立,退出循环。 while循环while循环,一直执行循环体的内容知道条件判断为假。尤其适合事先不知道迭代次数的情况。Swift提供两个格式的while: whilewhile condition { statements } 下面的游戏名字叫做《蛇和梯子》: 玩家的起始位置在0号格子,就是游戏面板的左下角。下面给出第一种掷色子的方式: var? ?square? = ?0 ?var? ?diceRoll? = ?0 ?while? ?square? < ?finalSquare? { ? ?// roll the dice ? ?if? ++?diceRoll? == ?7? { ?diceRoll? = ?1? } ? ?// move by the rolled amount ? ?square? += ?diceRoll ? ?if? ?square? < ?board?.?count? { ? ?// if we're still on the board,move up or down for a snake or a ladder ? ?square? += ?board?[?square?] ? } ?} ?println?(?"Game over!"?) 这是一个非常简单的掷色子方式。没有采用随机算法得到色子的点数,而是给出初始值0。每次循环都做一次自加操作,并作值检查,防止过大。当值过大(到7)的时候,被置为1。所以这个方式给出的色子点数序列一直都是这样的:“1,2,3,4,5,6,1,2……”。 do-whiledo-while是另外一种while循环。在判断循环条件前,先执行循环体内容,直到循环条件判断为假。 do { statements } while condition 下面还是以蛇和梯子为例,用do-while实现。finalSquare,board,square,和 diceRoll在循环前就被初始化好了: ?let? ?finalSquare? = ?25 ?var? ?board? = [?Int?](?count?: ?finalSquare? + ?1?,?repeatedValue?: ?0?) ?board?[?03?] = +?08?; ?board?[?06?] = +?11?; ?board?[?09?] = +?09?; ?board?[?10?] = +?02 ?board?[?14?] = -?10?; ?board?[?19?] = -?11?; ?board?[?22?] = -?02?; ?board?[?24?] = -?08 ?var? ?square? = ?0 ?var? ?diceRoll? = ?0 这个版本的游戏中,循环中第一步要做的就是判断是梯子还是蛇。没有梯子直通25号格子,所以不可能通过梯子赢得游戏,进而检查时梯子还是蛇是安全的。 ?do? { ? ?// move up or down for a snake or ladder ? ?square? += ?board?[?square?] ? ?// roll the dice ? ?if? ++?diceRoll? == ?7? { ?diceRoll? = ?1? } ? ?// move by the rolled amount ? ?square? += ?diceRoll ?} ?while? ?square? < ?finalSquare ?println?(?"Game over!"?) 在检查完是梯子还是蛇的语句执行完,开始摇色子,玩家按照摇出的点数走格子。循环体继续执行,直到循环结束。 条件语句两种条件语句:if和switch,前者使用简单的情况;后者适用多分枝的情况。 if当它之后的条件语句为真时,才会执行一系列语句。 ?var? ?temperatureInFahrenheit? = ?30 ?if? ?temperatureInFahrenheit? <= ?32? { ? ?println?(?"It's very cold. Consider wearing a scarf."?) ?} ?// prints "It's very cold. Consider wearing a scarf." 这个例子检查温度师傅低于华氏32度(水的冰点)。如果为真,打印;否则不做任何事。继续执行if括号后的内容。 temperatureInFahrenheit? = ?40 ?if? ?temperatureInFahrenheit? <= ?32? { ? ?println?(?"It's very cold. Consider wearing a scarf."?) ?} ?else? { ? ?println?(?"It's not that cold. Wear a t-shirt."?) ?} ?// prints "It's not that cold. Wear a t-shirt." 两个分支中的一个会被执行。因为温度是华氏40度,还没有冷到戴围巾的程度,所以进入else分支。 你可以将多个if语句一起使用,以使用条件的判断: temperatureInFahrenheit? = ?90 ?if? ?temperatureInFahrenheit? <= ?32? { ? ?println?(?"It's very cold. Consider wearing a scarf."?) ?} ?else? ?if? ?temperatureInFahrenheit? >= ?86? { ? ?println?(?"It's really warm. Don't forget to wear sunscreen."?) ?} ?else? { ? ?println?(?"It's not that cold. Wear a t-shirt."?) ?} ?// prints "It's really warm. Don't forget to wear sunscreen." 这里,一个if语句被添加以应对特定热的温度情况。结束的else被保留,打印的内容是不是特别冷夜不是特别热的情况。 最后的一个else是可选的,根据需要决定是否使用: temperatureInFahrenheit? = ?72 ?if? ?temperatureInFahrenheit? <= ?32? { ? ?println?(?"It's very cold. Consider wearing a scarf."?) ?} ?else? ?if? ?temperatureInFahrenheit? >= ?86? { ? ?println?(?"It's really warm. Don't forget to wear sunscreen."?) ?} 在这个例子中,温度不冷不热的情况下,没有任何信息输出。 switchswitch会判断一个值,对应若干匹配项,然后对应的内容会被执行。只匹配第一个合适的内容。switch语句是多分支情况下if的另一种写法。 switch some value to consider { case value 1: respond to value 1 case value 2,value 3: respond to value 2 or 3 default: otherwise,do something else } 每个switch语句由多个case构成,每个case以一个关键字开始。为了达到匹配特定数值的目的,Swift提供了多种方式匹配内容。下面将会一一讲到。 let? ?someCharacter?: ?Character? = ?"e" ?switch? ?someCharacter? { ?case? ?"a"?,?"e"?,?"i"?,?"o"?,?"u"?: ? ?println?(?"?(?someCharacter?)? is a vowel"?) ?case? ?"b"?,?"c"?,?"d"?,?"f"?,?"g"?,?"h"?,?"j"?,?"k"?,?"l"?,?"m"?,?"n"?,?"p"?,?"q"?,?"r"?,?"s"?,?"t"?,?"v"?,?"w"?,?"x"?,?"y"?,?"z"?: ? ?println?(?"?(?someCharacter?)? is a consonant"?) ?default?: ? ?println?(?"?(?someCharacter?)? is not a vowel or a consonant"?) ?} ?// prints "e is a vowel" 例子中第一个case匹配了所有英文的元音字母;第二个case匹配了所有的英文辅音字母。 没有默认的接续执行(No Implicit Fallthrough)和C语言和OC不同,Swift的switch不在默认依次执行每个case,而是只执行最初的匹配到的case,这样省得要写break了。这让switch变得更安全和简单,避免了多于一个的case会被执行。 每个case中至少要包含一条语句,否则不能通过编译: let? ?anotherCharacter?: ?Character? = ?"a" ?switch? ?anotherCharacter? { ?case? ?"a"?: ?case? ?"A"?: ? ?println?(?"The letter A"?) ?default?: ? ?println?(?"Not the letter A"?) ?} ?// this will report a compile-time error 多个内容可以匹配同一个case,只要用逗号分隔他们,像下面这样: switch some value to consider { case value 1,value 2: statements } 如果想要case顺序执行,可以使用关键字“fallthrough”,后文有述。 范围匹配switch的case可以按照一个范文匹配值。下面的例子将一个数字转换到一个范围内: let? ?count? = ?3_000_000_000_000 ?let? ?countedThings? = ?"stars in the Milky Way" ?var? ?naturalCount?: ?String ?switch? ?count? { ?case? ?0?: ? ?naturalCount? = ?"no" ?case? ?1?...?3?: ? ?naturalCount? = ?"a few" ?case? ?4?...?9?: ? ?naturalCount? = ?"several" ?case? ?10?...?99?: ? ?naturalCount? = ?"tens of" ?case? ?100?...?999?: ? ?naturalCount? = ?"hundreds of" ?case? ?1000?...?999_999?: ? ?naturalCount? = ?"thousands of" ?default?: ? ?naturalCount? = ?"millions and millions of" ?} ?println?(?"There are ?(?naturalCount?)? ?(?countedThings?)?."?) ?// prints "There are millions and millions of stars in the Milky Way." 元组你可以使用元组在一个case中尝试匹配多个值。每个元组的元素可以和一个值或者一个范围做比较。或者你可以使用”_”做通配符匹配任意值。 let? ?somePoint? = (?1?,?1?) ?switch? ?somePoint? { ?case? (?0?,?0?): ? ?println?(?"(0,0) is at the origin"?) ?case? (?_?,?0?): ? ?println?(?"(?(?somePoint?.?0?)?,0) is on the x-axis"?) ?case? (?0?,?_?): ? ?println?(?"(0,?(?somePoint?.?1?)?) is on the y-axis"?) ?case? (-?2?...?2?,-?2?...?2?): ? ?println?(?"(?(?somePoint?.?0?)?,?(?somePoint?.?1?)?) is inside the box"?) ?default?: ? ?println?(?"(?(?somePoint?.?0?)?,?(?somePoint?.?1?)?) is outside of the box"?) ?} ?// prints "(1,1) is inside the box" 例子中,switch的cse对应几种情况:原点、x轴、y轴、在以远点为中心的4*4正方形中、在正方形之外。 值绑定switch的case可以绑定值给临时的常量或者变量,这些临时敞亮或者变量可以在case块内使用。这被称作value binding,因为值给绑定到了常量或者变量之上,在case中使用。 let? ?anotherPoint? = (?2?,?0?) ?switch? ?anotherPoint? { ?case? (?let? ?x?,?0?): ? ?println?(?"on the x-axis with an x value of ?(?x?)?"?) ?case? (?0?,?let? ?y?): ? ?println?(?"on the y-axis with a y value of ?(?y?)?"?) ?case? ?let? (?x?,?y?): ? ?println?(?"somewhere else at (?(?x?)?,?(?y?)?)"?) ?} ?// prints "on the x-axis with an x value of 2" 例子中switch判断点是在x轴、y轴还是不在任何轴上。 where可以在switch的case语句中使用where添加附加的判断条件。 let? ?yetAnotherPoint? = (?1?,-?1?) ?switch? ?yetAnotherPoint? { ?case? ?let? (?x?,?y?) ?where? ?x? == ?y?: ? ?println?(?"(?(?x?)?,?(?y?)?) is on the line x == y"?) ?case? ?let? (?x?,?y?) ?where? ?x? == -?y?: ? ?println?(?"(?(?x?)?,?(?y?)?) is on the line x == -y"?) ?case? ?let? (?x?,?y?): ? ?println?(?"(?(?x?)?,?(?y?)?) is just some arbitrary point"?) ?} ?// prints "(1,-1) is on the line x == -y" 这个例子,switch判断点是在绿色的线上、 紫色的线上还是其他。 控制转移语句四个转移语句: continuecontinue告诉循环要停止现在做的事情,返回循环体开始,开始下一个循环。也就是说:我做完了本次循环。不离开循环。 下面的例子移除了一个小写字符串中的元音字母和空格,形成了一个字谜: let? ?puzzleInput? = ?"great minds think alike" ?var? ?puzzleOutput? = ?"" ?for? ?character? ?in? ?puzzleInput? { ? ?switch? ?character? { ? ?case? ?"a"?,?"u"?,?" "?: ? ?continue ? ?default?: ? ?puzzleOutput?.?append?(?character?) ? } ?} ?println?(?puzzleOutput?) ?// prints "grtmndsthnklk" 上面的代码在匹配到元音字母和空格时使用了continue关键字,导致当前循环直接进行到最后并且返回到循环体的开始进行下一次循环。这样让switch块匹配到元音字母和空格,不必将其他字母输出。 breakbreak会立即结束完整的控制流。break可以在switch语句或者循环语句中使用,当你在上述结构中想要终止继续执行的时候。 循环中的break循环中使用break,直接结束循环,循环结束标记最后一个}后的第一行将被继续执行。当前循环的后续内容不被执行,后续循环的内容也不被执行。 switch中的breakswitch中使用break会导致switch内容不再继续被执行,控制权交由switch末尾}后的第一行。 这个行为可以在想要忽略一个或多个case的时候使用。 因为Swift的switch要包含全部情形而且不允许有空的case存在,所以在需要直接表述特意匹配或者忽略某个case的时候特别有用。这样的具体作法是在case块中写break。当这个case被匹配到后,直接结束switch。 let? ?numberSymbol?: ?Character? = ?"三"? ?// Simplified Chinese for the number 3 ?var? ?possibleIntegerValue?: ?Int?? ?switch? ?numberSymbol? { ?case? ?"1"?,?"?"?,?"一"?,?"?"?: ? ?possibleIntegerValue? = ?1 ?case? ?"2"?,?"?"?,?"二"?,?"?"?: ? ?possibleIntegerValue? = ?2 ?case? ?"3"?,?"?"?,?"三"?,?"?"?: ? ?possibleIntegerValue? = ?3 ?case? ?"4"?,?"?"?,?"四"?,?"?"?: ? ?possibleIntegerValue? = ?4 ?default?: ? ?break ?} ?if? ?let? ?integerValue? = ?possibleIntegerValue? { ? ?println?(?"The integer value of ?(?numberSymbol?)? is ?(?integerValue?)?."?) ?} ?else? { ? ?println?(?"An integer value could not be found for ?(?numberSymbol?)?."?) ?} ?// prints "The integer value of 三 is 3." 这个例子会检查numberSymbol 判断它是拉丁语、阿拉伯语、汉语还是泰语的1-4数字。如果匹配到了,switch的一个case会给一个叫做possibleIntegerValue 的Int可选类型赋予恰当的值。 接续执行(Fallthrough)Swift的switch不依次接续执行case,而是指执行匹配到的第一个case就结束了整个switch。而C语言中你需要在每个case的结束添加break阻止默认接续执行。避免默认接续执行意味着Swift的switch比C语言更加简明,这样避免了不小心执行多个case的情况。 let? ?integerToDescribe? = ?5 ?var? ?description? = ?"The number ?(?integerToDescribe?)? is" ?switch? ?integerToDescribe? { ?case? ?2?,?3?,?5?,?7?,?11?,?13?,?17?,?19?: ? ?description? += ?" a prime number,and also" ? ?fallthrough ?default?: ? ?description? += ?" an integer." ?} ?println?(?description?) ?// prints "The number 5 is a prime number,and also an integer." 这个例子定义了一个字符串类型的变量并且给他赋值了。然后用switch语句检查integerToDescribe 的值。如果integerToDescribe 是一个素数,integerToDescribe 会被追加素数的标记内容。接下来使用了fallthrough关键字,接续执行default case。default里面给integerToDescribe 追加了一些值。switch就执行完了。 标签语句用Swift你可以在循环和switch中嵌套使用循环和switch,构建复杂的控制流结构。循环和switch语句都可以使用break语句提前结束。然而更有用的是明确在break的对象到底是哪个循环或者switch。类似的在一个多重循环中,表明continue语句影响的是哪个循环也是有用的。 为了获得上述目标,你可以给循环或者switch添加标签,然后是使用这个标签在break或者continue语句中。 label name: while condition { statements } 下面的例子使用break和continue以及添加了标签的while循环。案例还是前述的蛇和梯子的游戏。这次有了额外的规则: finalSquare,and diceRoll 的值初始化方式和先前一样: ?let? ?finalSquare? = ?25 ?var? ?board? = [?Int?](?count?: ?finalSquare? + ?1?,?repeatedValue?: ?0?) ?board?[?03?] = +?08?; ?board?[?06?] = +?11?; ?board?[?09?] = +?09?; ?board?[?10?] = +?02 ?board?[?14?] = -?10?; ?board?[?19?] = -?11?; ?board?[?22?] = -?02?; ?board?[?24?] = -?08 ?var? ?square? = ?0 ?var? ?diceRoll? = ?0 这个版本的游戏是哟给你了一个while循环和一个switch来实现游戏逻辑。while循环被添加了标签叫做gameLoop,表明这是游戏的主逻辑。 gameLoop?: ?while? ?square? != ?finalSquare? { ? ?if? ++?diceRoll? == ?7? { ?diceRoll? = ?1? } ? ?switch? ?square? + ?diceRoll? { ? ?case? ?finalSquare?: ? ?// diceRoll will move us to the final square,so the game is over ? ?break? ?gameLoop ? ?case? ?let? ?newSquare? ?where? ?newSquare? > ?finalSquare?: ? ?// diceRoll will move us beyond the final square,so roll again ? ?continue? ?gameLoop ? ?default?: ? ?// this is a valid move,so find out its effect ? ?square? += ?diceRoll ? ?square? += ?board?[?square?] ? } ?} ?println?(?"Game over!"?) 每次循环都会首先掷色子。没有直接移动玩家,而是用一个switch检查移动的结果,检查移动是否合法:
NOTE 如果上面的break语句不使用gameLoop标签,将会跳出switch语句,而不是while语句。使用gameLoop标签使得要结束的语句更加明确。 这里其实并不一定要使用gameLoop标签,当使用continue gameLoop来跳转到下一次循环时。在这个游戏中只有一个层循环,所以只使用continue并不会有任何歧义。然而这里并不妨碍使用continue时使用gameLoop标签。这样的好处都是使得逻辑清晰易读易懂。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |