6.11 练习

本章的示例代码位于仓库 ThinkJavaCode 的目录 ch06 中,有关如何下载这个仓库,请参阅前言中的“使用示例代码”一节。做以下的练习前,建议你先编译并运行本章的示例。

如果你还没有阅读 A.7 节,那么现在正是阅读的好时机。该节介绍了 JUnit——一款测试值方法的有效工具。

练习6-1

如果你对某种做法是否合法、不合法的结果是什么存在疑问,让编译器来解答是一种不错的方法。请尝试回答下面的问题。

(1) 如果调用一个值方法,但不用它返回的结果,即既没有将其赋给变量,也没有将其用于表达式中,结果将如何呢?

(2) 如果将一个 void 方法用于表达式中,结果将如何呢?例如,尝试执行代码 System.out.println("boo!") + 7

练习6-2

编写一个名为 isDivisible 的方法,让它接受两个整数——nm,并在 n 能被 m 整除时返回 true,否则返回 false

练习6-3

给定 3 根棍子,可能能够将它们排列成三角形,也可能不行。例如,如果其中一根棍子长 12 英寸,另外两根都为 1 英寸,那么就无法将 3 根棍子相互相连。给定任意 3 根棍子的长度,可通过下面的简单测试来判断它们能否组成三角形:

只要其中 1 根棍子的长度大于其他 2 根棍子的总长,它们就无法组成三角形。

请编写一个名为 isTriangle 的方法,让它接受 3 个整数参数,并根据长度分别为这 3 个整数的棍子能否组成三角形而返回 truefalse。这个练习的重点是用条件语句编写值方法。

练习6-4

很多计算都可用“乘加”运算来更简洁地表示出来,这种运算接受 3 个操作数,并计算 a * b + c 的结果。有些处理器甚至提供了对浮点数执行这种运算的硬件实现。

(1) 创建一个程序,并命名为 Multadd.java。

(2) 编写一个名为 multadd 的方法,让它接受 3 个 double 参数,并返回 a * b + c

(3) 编写一个 main 方法,并在其中测试方法 multadd:调用 multadd 并传递几个简单的实参,如 1.02.03.0

(4) 另外,在 main 中用方法 multadd 来计算下述表达式的值:

\begin{aligned}&\sin\frac{\pi}{2}+\frac{\cos\frac{\pi}{4}}{2}\\&\log10+\log20\end{aligned}

(5) 编写一个名为 expSum 的方法,让它接受一个 double 参数,并调用 multadd 来计算下述表达式的值:

x{\rm e}^{-x}+\sqrt{1-{\rm e}^{-x}}

在这个练习的最后一部分,你需要编写一个方法,这个方法要能调用你编写的另一个方法。在这种情况下,最好先仔细测试要调用的方法,否则你可能面临同时调试两个方法的困境。

这个练习的目的之一是锻炼你的模式匹配能力——能够看出要解决的问题是通用问题的特例。

练习6-5

以下程序的输出是什么?

  1. public static void main(String[] args) {
  2. boolean flag1 = isHoopy(202);
  3. boolean flag2 = isFrabjuous(202);
  4. System.out.println(flag1);
  5. System.out.println(flag2);
  6. if (flag1 && flag2) {
  7. System.out.println("ping!");
  8. }
  9. if (flag1 || flag2) {
  10. System.out.println("pong!");
  11. }
  12. }
  13. public static boolean isHoopy(int x) {
  14. boolean hoopyFlag;
  15. if (x % 2 == 0) {
  16. hoopyFlag = true;
  17. } else {
  18. hoopyFlag = false;
  19. }
  20. return hoopyFlag;
  21. }
  22. public static boolean isFrabjuous(int x) {
  23. boolean frabjuousFlag;
  24. if (x > 0) {
  25. frabjuousFlag = true;
  26. } else {
  27. frabjuousFlag = false;
  28. }
  29. return frabjuousFlag;
  30. }

这个练习旨在确保你明白逻辑运算符和值方法的执行流程。

练习6-6

在这个练习中,你可以用栈图来帮助理解下述递归程序的执行流程:

  1. public static void main(String[] args) {
  2. System.out.println(prod(1, 4));
  3. }
  4. public static int prod(int m, int n) {
  5. if (m == n) {
  6. return n;
  7. } else {
  8. int recurse = prod(m, n - 1);
  9. int result = n * recurse;
  10. return result;
  11. }
  12. }

(1) 绘制一个栈图,指出对 prod 的最后一次调用结束前,这个程序的状态是什么样的。

(2) 这个程序的输出是什么?(先尝试用纸和笔来回答这个问题,再通过运行代码来检查答案。)

(3) 简单地说说 prod 是做什么的(无需涉及其工作原理的细节)。

(4) 修改方法 prod,删除其中的临时变量 recurseresult提示else 分支只需要一行代码。

练习6-7

编写一个名为 oddSum 的递归方法,它接受一个正奇数——n,并返回 1~n(闭区间)所有奇数的和。请先考虑基线条件,并用临时变量调试解决方案。每次调用 oddSum 时都打印参数值 n 可能大有帮助。

练习6-8

这个练习的目标是将递归定义转换为 Java 方法。阿克曼函数的定义如下(其中 mn 都是非负整数):

6.11 练习 - 图3

请编写一个名为 ack 的方法,让它接受两个 int 参数,然后计算并返回阿克曼函数的值。

测试你编写的 ack 方法:在 main 中调用它,并显示它返回的值。请注意,阿克曼函数值的递增速度非常快,测试时应使用较小的 mn(不超过 3)。

练习6-9

编写一个名为 power 的递归方法,它接受 double 参数 xint 参数 n,并返回 xn 的值。

提示:这种运算的递归定义为 xn = x·xn-1。另外别忘了,零以外的任何数字的 0 次方都为 1。

选做题:n 为偶数时,利用公式 xn=(xn/22 来提高这个方法的效率。