沃尔特·萨维奇的书 《Problem Solving with C++ 》第五章在介绍函数的抽象原则时,提到:函数的注释中应该包括了这个函数被调用的“前提”和“结果”,正确的前提将保证得到正确的结果,否则则是错误的调用。
在写完程序进行调试(确定程序编写无误)的时候,除了调用编译器的调试组件随时查看变量值,还可以检查一个函数的调用前提是否被满足。这时可以使用一个类似于函数的工具:“宏”,来完成判断,这个宏就被称为 assert 宏,翻译过来就是“断言”宏,用来判断它附加的前提表达式(是个布尔逻辑表达式)是否成立。
也就是写作 assert(前提-布尔表达式 is true); 。如果断言宏附加的布尔表达式不满足,则程序会以非0值退出。
下面是一段应用了 assert 宏的程序,其中为了求一个给定的正数的平方根,用了牛顿的“递归公式法”,这时要求“给定的数和递归的次数都为正数”;断言宏断言这个前提是满足的。
#include <iostream>
// 这是一个用 “牛顿法”公式 迭代求解某个正数的平方根的程序#define NDEBUG
#include <cassert> // 使用 assert 宏 需要引入的头文件void getInput(double& num, int& i);
// pre: 键盘输入 num - 需要求平方根的数 i - 公式计算迭代的次数
// post:num 和 i 分别被设定为输入值void introduction();
// pre: 无
// post: 介绍一下程序功能double newtonSqroot(double n, int num_iterations);
// pre: n是正数 num_iterations是正整数
// post: 返回 n 的平方根 (通过牛顿公式的 num_iterations 次迭代来估算)void giveOutput(double num, int i, double sq_root);
// pre: num - 需要求平方根的数 i - 计算迭代的次数 sq_root - 求得的平方根
// post:屏幕输出 num i sq_root 的值int main()
{double num, ans;int i; // ans 是 num 的平方根 (经过了i次迭代求得的近似解)introduction();getInput(num, i);ans = newtonSqroot(num, i);giveOutput(num, i, ans);return 0;
}void getInput(double& num, int& i)
{using namespace std;cout << "请输入您要求平方根的数(需要大于0):";cin >> num;cout << "为了求这个平方根,您希望经过几轮计算?(算得越多越接近真实值)\n";cin >> i;
}void introduction()
{using namespace std;cout << "依据牛顿法,每将整数N上一次求出的近似平方根x带入公式 (1/2)(x + N/x) \n" << "求得的值会都比x更接近N的平方根。 本程序可以求一个整数的平方根的近似值: \n";
}double newtonSqroot(double n, int num_iterations)
{double answer = 1; // 既是n=1时的答案,也是第0次迭代时的初始值int i = 0;assert((n > 0) && (num_iterations > 0)); // 如果调用函数的前提不满足,这行语句将使程序因断言失败而退出while (i < num_iterations){answer = 0.5 * (answer + n / answer);i++;}return answer;
}void giveOutput(double num, int i, double sq_root)
{using namespace std;cout.setf(ios::fixed);cout.setf(ios::showpoint);cout.precision(4);cout << "经过 " << i << " 次计算,"; cout << num << " 的平方根估计值为 " << sq_root << endl;cout << "感谢使用! \n";
}
递归的公式在代码中给出了,就是 \(sqrt_{i+1} = \frac{1}{2} \{ sqrt_i + \frac{n}{sqrt_i} \}\).
需要注意的除了 为了使用 assert,要引入NDEBUG (在代码开头有一行 #define NDEBUG),就可以让代码中的所有 assert 宏失效,也就是即使 assert宏 假设的前提不成立,程序也不会终止,而是就像调用assert宏的语句不存在一样继续运行。所以,要让文中的代码里的assert起到检查前提是否满足的调试功能,需要删掉开头 #define NDEBUG 这一句。
大概就是这样。
