跳至内容
Topos Uranos

摘要

这节课开启本课程的第五单元,主题是函数。它说明本单元的用途:教会把一个问题分解为有名字的部分,每一部分都有接口和契约,并通过把某些问题化归为它们自身的一个更小的实例来求解,也就是递归;介绍本单元的四节课以及把它们串起来的线索;回顾本单元假定已有的知识,尤其是第 4 单元的循环及其不变式;提出本单元要解决的问题,即一个程序把同一个计算复制了一遍,又因复制时的疏忽而给出错误答案,编译器却一言不发;预先介绍契约(前置条件和后置条件),作为本单元规定每一部分规格的形式;并说明本单元通向何处,包括后面各单元以及“C++ 程序设计 II”课程。

学习目标

  1. 描述本单元的内容顺序,并说明每节课为后一节课提供了什么。
  2. 回顾第 4 单元的算法(循环的跟踪、它的不变式和它的变式),必要时加以复习。
  3. 借助一个例子,预先理解契约的思想:程序的一个部分对使用它的人提出什么要求,又反过来向其保证什么。
  4. 认清学完本单元时应当会做什么,并把它与后面各单元将要学习的内容区分开来。

入门测评

在学习本单元之前,先测一测自己的起点是有益的。这项测评是自我测评:不计分,唯一的目的是为学习指引方向。前五道题检查本单元假定已经掌握的内容:循环的跟踪、它的不变式、一个规格说明的前置条件、初始化时对值的复制,以及在第 4 单元的一个程序里找出一个有自己的输入和输出的计算。其后的五道题按课的顺序涉及本单元的核心思想:调用栈、实参的传递、计算与呈现的分离、阶乘的递归定义,以及一个调用自身的函数的执行。没有人必须现在就答对,其中有几道题里,“我还不知道”是诚实的回答:每个答案,无论对错,都会解释要点,并指出讲解这一要点的那节课。同样的能力会在单元末尾的总结课中再次测量。

入门测评

开始本单元之前,先测一测你的起点。这是一次简短的自我测评:不计分,结果只保存在你的浏览器中。

  1. 第 1 题来自前面的单元int n{305}; int c{1}; while (n >= 10) { n = n / 10; ++c; } std::println("{}", c); 输出什么?

  2. 第 2 题来自前面的单元在循环 int total{0}; int k{1}; while (k <= n) { total += k; ++k; } 中(n≥0n \geq 0),每次计算条件时,哪个关系成立?

  3. 第 3 题来自前面的单元除法的规格说明是:“给定整数 a≥0a \geq 0 和 b>0b > 0,求 qq 和 rr,使得 a=b⋅q+ra = b \cdot q + r 且 0≤r<b0 \leq r < b”。它的前置条件是什么?

  4. 第 4 题来自前面的单元执行 int a{3}; int b{a}; b = 7; 之后,a 的值是多少?

  5. 第 5 题来自前面的单元第 4.4 课中输出小于 100 的素数的程序,有一个遍历候选数的循环,里面还有一个试验其因数的循环。哪一部分是有自己的输入和输出、值得起个名字的计算?

  6. 第 6 题一个程序从 main 开始;main 调用函数 g,g 又调用另一个函数 h。在 h 执行期间,哪些调用正在进行?

  7. 第 7 题有函数 int f(int x) { x = x + 1; return x; },那么 int a{3}; int b{f(a)}; std::println("{} {}", a, b); 输出什么?

  8. 第 8 题一个程序读入一个日期,要输出它是星期几。哪种分解把计算与呈现分开了?

  9. 第 9 题下列哪个定义能定义 n!n!(对一切自然数 nn)?

  10. 第 10 题有 int f(int n) { if (n == 0) { return 0; } return n + f(n - 1); },那么 std::println("{}", f(3)); 输出什么?