安安, 其实这题不需要真的执行啦! 只要善用简单的静态分析 (static
analysis) 技巧就可以知道问题在哪边. 首先是 for 循环的基本定义, 我们可
以用判断程式码品质时常用的方法: 资料视觉化 (data visualization) 来为
程式码片段做着色. for 循环里的程式码依照责任可以分成四个区块:
for ( (1) ; (2) ; (3) ) {
(4)
}
虽然通常每个区块做的事情都不同, 但求值顺序却是固定的, 如果我们想知道
循环的商业逻辑, 我们会直接看 (4) , 想知道问题的范围/大小就看 (2) , 依
此类推. while 循环也有这四个区块存在, 只是写得比较分散一点:
(1)
while ( (2) ) {
(4)
(3)
}
从上面可以看到, while 循环的 (4) 和 (3) 同样都写在循环的本体内, 在阅
读上和 for 相比多了区分两区块的步骤, 而且 (1) 容易和前面的程式码混在
一起, 可读性较差. 因为两种循环是可以互转的, 在发现有这个问题时, 就可
以尝试将循环改写, 改写前先将程式码着色 (从 for 循环出发):
int i=0,a;
for(a=0;i<n;a++) {
if(a%3==2 && a%5==3 && a%7==2)
i++;
}
如果转成 while 循环的话会变成这个样子:
int i=0,a=0;
while (i<n) {
if(a%3==2 && a%5==3 && a%7==2)
i++;
a++;
}
有没有发现这个 while 循环和你写的不太一样? 这个就是问题所在: 它们的结
构是不一样的. 程式码结构不同, 导致了求值顺序甚至是语意的改变, 所以你
跑出来的结果才会有差. 为了验证这件事情, 我们可以用简易的方法来比较程
式码结构: 将已经着色的程式码区块依照求值顺序排成有多种颜色的线段, 然
后比较两线段的颜色差异. 假设循环迭代 3 次, 你的 for 循环和 while 循环
画成线段后会长成下面的样子:
(for-loop)
(while-loop)
————————————————————————→
time
如上图所示, 排出来的线段颜色相同, 才表示这两份程式码做的事情是等价的;
你的例子因为 a++ 和 if 语句 顺序对调的缘故, 已经产生出不同的程式.