── 章节回顾 ──
CPU 在程序中执行的具体语句序列称为程序的执行路径。直线式程序每次运行时路径完全相同。
控制流语句(亦称流程控制语句)允许程序员改变正常的执行路径。当控制流语句使程序开始执行非顺序的指令序列时,称为一次“分支”。
条件语句用于指定其关联语句是否应当执行。
- if语句在条件为真时执行关联语句;
- else语句在条件为假时执行;
- 可将多个 if与else链式组合。
- “悬垂 else”指 else与哪个if配对出现歧义;悬垂else会与同一作用域内最后一个未匹配的if配对。因此,将if语句体置于代码块中即可避免悬垂 else。
空语句仅由一个分号构成,什么也不做;当语法要求存在语句而程序员无需其执行任何操作时,可使用空语句占位。
switch 语句为在多项匹配值中进行选择提供了更简洁、高效的手段,仅适用于整型。case 标签用于标识待匹配值;若找不到匹配 case,则执行 default 标签下的语句。
- 当执行流从某标签下流入下一标签下的语句时,称为“贯穿(fallthrough)”。可用 break(或return)防止贯穿,也可用[[fallthrough]]属性显式标记有意的贯穿。
goto 语句允许程序跳转到代码中任意位置(向前或向后)。通常应避免使用,以免产生“意大利面条代码”——执行路径如同一碗缠绕的面条。
while 循环使程序在条件为真时反复执行。条件在循环开始前求值。
- 无限循环指条件恒为真的循环;除非其他控制流语句介入,否则会永远执行。
- 循环变量(亦称计数器)是用于记录循环执行次数的整型变量;每次循环执行称为一次“迭代”。
do-while 循环与 while 类似,但其条件在循环体执行后求值。
for 循环最为常用,适用于需固定次数循环的场景。
- 差一错误指循环次数多一次或少一次。
break 语句可立即跳出 switch、while、do-while、for(以及尚未介绍的范围 for)循环。continue 语句立即进入下一次循环迭代。
终止(halt)语句用于结束程序。
- 正常终止:程序按预期退出,状态码指示成功或失败。std::exit()在main结束时自动调用,也可显式调用以终止程序;其执行部分清理,但不销毁局部变量,也不展开调用栈。
- 异常终止:程序遇到意外错误被迫关闭。可调用 std::abort实现异常终止。
算法是为解决某问题或产生有用结果而制定的有限指令序列。
- 若算法在多次调用间保留信息,则为“有状态”;否则为“无状态”。
- 若给定输入总能产生相同输出序列,则称算法为“确定性”。
伪随机数生成器(PRNG)是一种算法,其输出序列的统计特性模拟随机数序列。实例化 PRNG 时,可提供初始值或值集(称为随机种子或种子)以初始化其状态,称为“播种”。若种子位数小于 PRNG 状态位数,则称 PRNG“欠播种”。PRNG 开始重复前的序列长度称为“周期”。
随机数分布将 PRNG 输出转换为其他分布。均匀分布是在区间 [X, Y](含端点)内等概率产生输出的随机数分布。
测验时间
提示:自本测验起难度逐步提升,但请相信你能完成!
问题 1
在第 4 章总结与测验中,我们编写了一个模拟球从塔上坠落的程序。由于当时尚未学习循环,球只能下落 5 秒。
请将下方程序修改为:球持续下落直至触地,并应用所有已介绍的最佳实践(命名空间、constexpr 等)。
#include <iostream>
double getTowerHeight()
{
    std::cout << "Enter the height of the tower in meters: ";
    double towerHeight{};
    std::cin >> towerHeight;
    return towerHeight;
}
double calculateBallHeight(double towerHeight, int seconds)
{
    const double gravity { 9.8 };
    const double fallDistance { gravity * (seconds * seconds) / 2.0 };
    const double ballHeight { towerHeight - fallDistance };
    return ballHeight < 0.0 ? 0.0 : ballHeight;
}
void printBallHeight(double ballHeight, int seconds)
{
    if (ballHeight > 0.0)
        std::cout << "At " << seconds << " seconds, the ball is at height: "
                  << ballHeight << " meters\n";
    else
        std::cout << "At " << seconds << " seconds, the ball is on the ground.\n";
}
void calculateAndPrintBallHeight(double towerHeight, int seconds)
{
    printBallHeight(calculateBallHeight(towerHeight, seconds), seconds);
}
int main()
{
    const double towerHeight{ getTowerHeight() };
    for (int sec{ 0 }; ; ++sec)
    {
        const double height{ calculateBallHeight(towerHeight, sec) };
        printBallHeight(height, sec);
        if (height <= 0.0) break;
    }
    return 0;
}
问题 2
质数是指大于 1 且仅能被 1 和自身整除的自然数。
请补全下列程序,实现使用 for 循环的 isPrime() 函数。成功后将输出 “Success!”。
#undef NDEBUG
#include <cassert>
#include <cmath>
#include <iostream>
bool isPrime(int x)
{
    if (x < 2) return false;
    if (x == 2) return true;
    if (x % 2 == 0) return false;
    for (int i{ 3 }; i * i <= x; i += 2)
        if (x % i == 0) return false;
    return true;
}
int main()
{
    assert(!isPrime(0));
    assert(!isPrime(1));
    assert(isPrime(2));
    assert(isPrime(3));
    assert(!isPrime(4));
    assert(isPrime(5));
    assert(isPrime(7));
    assert(!isPrime(9));
    assert(isPrime(11));
    assert(isPrime(13));
    assert(!isPrime(15));
    assert(!isPrime(16));
    assert(isPrime(17));
    assert(isPrime(19));
    assert(isPrime(97));
    assert(!isPrime(99));
    assert(isPrime(13417));
    std::cout << "Success!\n";
    return 0;
}
问题 3
实现 Hi-Lo 猜数游戏。程序随机选取 1–100 之间的整数,玩家有 7 次机会猜测。
- 猜错则提示“过高”或“过低”;
- 猜对则提示胜利;
- 用完 7 次仍未猜中则提示失败并公布答案;
- 每局结束询问是否再玩,输入非 'y'/'n'则重复询问。
使用 Random.h。
示例会话见原题,额外加分:将最小值、最大值及猜测次数设为可配置参数。
#include <iostream>
#include "Random.h"
void playGame(int min, int max, int guesses)
{
    int target{ Random::get(min, max) };
    for (int attempt{ 1 }; attempt <= guesses; ++attempt)
    {
        std::cout << "Guess #" << attempt << ": ";
        int guess{};
        std::cin >> guess;
        if (guess == target)
        {
            std::cout << "Correct! You win!\n";
            return;
        }
        else if (guess < target)
            std::cout << "Your guess is too low.\n";
        else
            std::cout << "Your guess is too high.\n";
    }
    std::cout << "Sorry, you lose. The correct number was " << target << ".\n";
}
int main()
{
    constexpr int min{ 1 }, max{ 100 }, guesses{ 7 };
    while (true)
    {
        std::cout << "Let's play a game. I'm thinking of a number between "
                  << min << " and " << max << ". You have " << guesses
                  << " tries to guess what it is.\n";
        playGame(min, max, guesses);
        char again{};
        do
        {
            std::cout << "Would you like to play again (y/n)? ";
            std::cin >> again;
        } while (again != 'y' && again != 'n');
        if (again == 'n')
        {
            std::cout << "Thank you for playing.\n";
            break;
        }
    }
    return 0;
}
