【Swift 3 && C++11】<第一部分> 概览(2): Swift控制流与C++控制流

流程图

控制流, 就是程序中的控制语句, 如 if, switch, for, while 等.但是在 Swift 和 C++中控制流有一些不同.

控制流 Swift C++
条件 if, switch if, switch
循环 for-in ,while,repeat-while for, 范围 for, while, do-while

Swift 中条件表达式和循环变量的括号可以省略, 但是大括号必须要写:

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScore += 3
    } else {
        teamScore += 1
    }
}
print(teamScore)

C++中圆括号是必须要写的, 大括号反而可以省略. 但要注意的是, 大括号括起来的众多语句, 整体表示一条复合语句, 一个; 省略了大括号, 相应的语句体只有一条语句起作用:

#include <iostream>
using namespace std;

int main() {
    
    const int individualScores[5] = {75, 43, 103, 87, 12,};
    auto teamScore = 0;
    for (auto score : individualScores) {
        if (score > 50) {
            teamScore += 3;
        }else {
            teamScore += 1;
        }
    }
    
    cout << teamScore << endl;
    
    if (teamScore > 10) // 省略了大括号
        cout << "teamScore 大于10" <<  endl; // 此时这里只能写一条语句
    else // 省略了大括号
        cout << "teamScore 不大于10" << endl; // 此时这里只能写一条语句
    
    return 0;
}

if 语句

Swift 中 if 语句的条件必须是一个布尔表达式. 像if score {....}编译器会提示出错.

而不像在** C++中 ** score 可以是表达式或变量, 会隐式的转换成布尔类型, 当然前提是这个表达式变量必须能够支持这种转换.

Swift中可以一起使用iflet 来处理值缺失的情况. 一个可选值是一个具体的值或者nil 来表示值缺失.在类型后面加一个问号来标记这个变量的值是可选的.

var optionalString: String? = "Hello"
print(optionalString == nil)

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
// 如果变量的可选值是 nil, 条件会判断为 false, 大括号中的代码会被跳过.
// 如果不是 nil, 会将值赋给 let 后面的常量, 这样在代码块中就可以使用这个值了.
if let name = optionalName { // 这里使用 var 也是可以的
    greeting = "Hello, \(name)"
}

练习: 把 optionalName 改成 nilgreeting会是什么?添加一个 else 语句,当 optionalNamenil 时给greeting赋一个不同的值。

另一种处理可选值的方法是通过使用??来提供一个默认值.如果可选值缺失的话, 可以使用默认值来代替.

let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"

注意 C++14中添加了可选值

C++中 if,switch,whilefor 语句的控制结构内可以定义变量. 定义在控制结构中的变量只在相应的语句的内部可见,一旦语句结束,变量也就超出其作用范围了:

#include <iostream>
using namespace std;

int main() {
    
    if (auto var = 0) // 变量 var 仅在该 `if-else` 语句内部可见
        cout << var << endl;
    
    return 0;
}

练习: 在上面的小例子中, 添加一个 else 分支,在大括号中打印 var 的值, ...else {...}.

switch 语句

Swift 中switch 支持任意类型的数据以及各种比较操作 —— 不仅仅是整型数以及测试相等。

let vegetable = "red pepper"
switch vegetable { // 关于 switch 你可以探索更多, 比如调换 case 语句的位置.
    case "celery":
        print("Add some raisins and make ants on a log.")  // 运行 switch 中匹配的子句之后, 程序会退出 switch, 并不会继续向下运行, 所以不需要在每个子句结尾写 break.
    case "cucumber", "watercress":
        print("That would make a good tea sandwich.")
    case let x where x.hasSuffix("pepper"): // 注意此处的 let 中是如何使用的,它将匹配的等式的值赋给常量 x; 此处的 let当然也可以使用 var 代替
        print("Is it a spicy \(x)?")
    default:
        print("Everything tastes good in soup.")
}

练习: 删除 default 语句,看看会有什么错误?

C++中switch 语句则不同. switch 括号中的表达式将转换为整型,然后与每个case 标签的值进行匹配. 从这点上看, C++的 switch语句功能没有 Swift 的多, 还存在更多的限制 :

#include <iostream>
using namespace std;

int main() {
    
    auto ch = 'a';
    switch (ch) {
        case 'a':
            cout << "ch 为字符 a" << endl;
            break;
        case 'b':
            cout << "ch 为字符 b" << endl;
            break;
        case 10:
            cout << "ch 的值为10" << endl;
            break;
        default:
            cout << "所有的 case 标签都没有被匹配" << endl;
            break;
    }
    
    return 0;
}

switch 语句会首先对ch 求值, 注意括号中的表达式可以是一个初始化的变量声明. 表达式的值转换成整数类型,然后与每个 case标签的值比较.

如果表达式和某个 case 标签的值匹配成功,程序从该标签之后的第一条语句开始执行, 知道到达了 switch 的结尾或者是遇到一个 break 语句为止.

break语句的作用是中断当前的控制流, 通常情况下, switch 常常与 break一起使用.

case 关键字和它对应的值一起被称为case 标签. case 标签必须是整形表达式

练习: 尝试删除上面例子中的每个 break, 会发生什么?

for语句的变式

Swift 中你可以使用for-in 来遍历字典. 此时需要两个变量来表示字典的键值对. 字典是一个无序集合,所以他们的键值对以任意顺序迭代结束.

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)

练习: 练习: 添加另一个变量来记录最大数字的种类(kind),同时仍然记录这个最大数字的值。

C++中你可以使用范围 for 来遍历序列,当然使用传统的for 语句也可以达到同样的效果:

#include <iostream>
#include <vector>
#include <map>
#include <string>
using namespace std;

int main() {
    
    map<string, vector<int>> interestingNumbers = {
        {"Prime", {2, 3, 5, 7, 11, 13}},
        {"Fibonacci", {1, 1, 2, 3, 5, 8}},
        {"Square", {1, 4, 9, 16, 25}},
    };
    
    // 范围 for 语句遍历序列
    for (const auto map_value : interestingNumbers) { // map_value 将是一个 pair 类型的值
        cout << map_value.first << ": "; // pair 包含两个数据成员 first 和 second, 使用点语法 . 可以得到它们
        for (const auto item : map_value.second)
            cout << item << " ,";
        cout << endl;
    }
    
    // 传统 for语句遍历序列
    // interestingNumbers.begin()将得到 map 的开头—— 它是一个迭代器(即指针), interestingNumbers.end()则得到 map 的结尾
    for (auto map_value = interestingNumbers.begin(); map_value != interestingNumbers.end(); ++map_value ) {
        cout << (*map_value).first << ": "; // 解引用这个迭代器将得到 pair 类型的值
        for (auto item = map_value->second.begin(); item != map_value->second.end(); ++item) // 而interestingNumbers 中关键字对应的值是一个 vector, 同样也可以得到它的开头和结尾
            cout << *item << " ,"; // 解引用 vector 的迭代器将得到其中的值
        cout << endl;
    }
    
    return 0;
}

map 是一个有序的不重复的关联数组, 当对map 进行遍历的时候, map_value 将是一个pair 类型的值, pair 包含两个数据成员firstsecond, 使用点语法.可以得到它们. 其中first 表示 map 中关键字,second 表示map中关键字所关联的值.

上面例子中, map_valuesecond 是一个vector, 它也是一个序列,可以再次使用范围 for 遍历其中的元素.

interestingNumbers.begin()表示调用interestingNumbers中的begin成员函数, 它将返回关联数组interestingNumbers的开头 —— 它是一个迭代器(即指针), 关联数组interestingNumbers的结尾则由end 成员函数表示.

解引用map迭代器得到的是pair类型的值,所以想要得到里面的键值对,只需要使用点语法即可. 而map_value->second 则等同于(*map_value).second .

vectormap 或者其他容器都拥有 beginend成员函数. 不过解引用vector的迭代器*item将得到其中的值.

联系: 怎样修改代码才能将打印的结果变为这样:

{ 
   { Fibonacci, { 1 ,1 ,2 ,3 ,5 ,8 ,} },
   { Prime, { 2 ,3 ,5 ,7 ,11 ,13 , } },
   { Square, { 1 ,4 ,9 ,16 ,25 , } },
}

提示:请手动控制 cout 的输出格式

while、do-while 语句的变式

Swift中可以使用while, repeat-while 来重复运行一段代码知道不满足条件为止. 循环的条件也可以在结尾, 这样可以保证能够至少循环一次.

var n = 2
while n < 100 {
    n = n * 2
}
print(n)

var m = 2
repeat {
    m = m * 2
} while m < 100
print(m)

你也可以在循环中使用..<来表示一个范围.

var total = 0
for i in 0..<4 {
    total += i
}
print(total)

使用..<创建的范围包含下界,但不包含上界, 使用...将包含上界也包含下界 —— 类似数学中的区间[0, 4), [0, 4].

C++中则可以使用while, do-while 做到同样的目的, 但没有什么快捷的方式创建一个范围. 需要注意的是, 由于 C++中的大括号可以省略, 只能使用一条单独的语句作为循环体; do-while语句需要在最后写一个分号;表示语句结束:

#include <iostream>
using namespace std;

int main() {
    
    auto n = 2;
    while (n < 100) {
        n = n * 2;
    } // C++中一对大括号 { } 本身就表示一个复合语句, 一个块. 因此就不必在后面加上分号, 当然加上分号也不影响.
    cout << n << endl;
    
    auto m = 2;
    do
        m = m * 2; // 此处只能用一条单独的语句作为循环体
    while (m < 100); // C++中分号 ; 才表示一个语句, 所以此处需要加上分号
    cout << m << endl;
}

练习: 分别粘贴下面的代码在 Swift 中运行, 将会得到什么错误提示?

var index: Int
for index = 0; index < 3; ++index {
    print("index is \(index)")
}
var index = 0
do {
    index += 2
} while index < 100
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • Swift 提供了类似 C 语言的流程控制结构,包括可以多次执行任务的for和while循环,基于特定条件选择执行...
    穷人家的孩纸阅读 688评论 1 1
  • Swift提供了多种控制流声明。包括while循环来多次执行一个任务;if,guard和switch声明来根据确定...
    BoomLee阅读 1,924评论 0 3
  • 本章将会介绍 控制流For-In 循环While 循环If 条件语句Switch 语句控制转移语句 continu...
    寒桥阅读 709评论 0 0
  • [The Swift Programming Language 中文版]本页包含内容: Swift提供了多种流程控...
    风林山火阅读 547评论 0 0
  • 一轮皎月高挂在空中,我坐在月下,仿佛听见了一记寒钟。 钟声仿佛从遥远的深处传来,在山的那边,跨越了山林,跨越了高楼...
    Surwin阅读 436评论 0 1