← All Units← 返回单元列表 ← Course Hub← 课程主页
A P  C O M P U T E R  S C I E N C E  A
Unit 2 · Solutions第 2 单元 · 解析

Selection and Iteration — Solutions选择与循环 —— 解析

Companion to the AP-Style MC & Free-Response Practice SetAP 风格选择题与自由回答题练习的解析配套

MEDIUM HARD AP MC AP FRQ

Unit 2: Selection and Iteration第 2 单元:选择与循环CSA



MULTIPLE CHOICEWorked Answers详细解析

Multiple Choice — Worked Answers选择题(Multiple Choice)—— 详细解析

Each item restates the prompt and choices, marks the correct letter, and gives a brief justification. Trap distractors are called out where useful.每道题重述题干与选项、标出正确答案字母,并给出简明解析;对容易混淆的干扰项(trap distractor)单独提示。

Q1MEDIUMAP MCShort-Circuit &&

x = 0; if (x != 0 && 10/x > 0) … → result?x = 0;if (x != 0 && 10/x > 0) … → 结果?

Answer:答案: (B)
&& short-circuits: if the left operand is false, the right operand is not evaluated.
  • x != 0 is false (since x = 0).
  • The whole && is therefore false; 10 / x is never computed.
  • The else branch runs: no is printed.
Trap (C): would only fire if the operands were evaluated left-to-right without short-circuit, which is not how Java's && works. This "guard-then-use" idiom is the standard way to protect against division-by-zero and null dereference.
&& 是短路求值(short-circuit evaluation):如果左操作数为 false,右操作数根本不会被计算。
  • x != 0 为 false(因为 x = 0)。
  • 整个 && 直接为 false;10 / x 永远不会被求值。
  • else 分支执行:打印 no。
陷阱 (C):只有在没有短路、按从左到右全部求值时才会触发——但 Java 的 && 并不是那样工作。这种"先保护、后使用"的写法是防止除以零和空引用解引用的标准技巧。
Q2MEDIUMAP MCfor Loop Count

for (int i = 5; i <= 20; i += 3) — count iterations?for (int i = 5; i <= 20; i += 3) —— 循环了多少次?

Answer:答案: (B)
Enumerate the values of i that satisfy i <= 20:
  • i = 5, 8, 11, 14, 17, 20 — six values
  • Next would be 23, which fails the test.
Formula: number of iterations = ⌊(end − start) / step⌋ + 1 when end is achievable. Here ⌊(20 − 5) / 3⌋ + 1 = 5 + 1 = 6. Trap (A) drops the inclusive endpoint; (C) over-counts by one (classic fencepost error).
列举所有满足 i <= 20 的 i:
  • i = 5, 8, 11, 14, 17, 20 —— 六个值
  • 下一个是 23,不再满足条件。
公式:当 end 恰好可达时,迭代次数 = ⌊(end − start) / step⌋ + 1。这里 ⌊(20 − 5) / 3⌋ + 1 = 5 + 1 = 6。陷阱 (A) 漏掉了能取到的右端点;(C) 多算了一次(典型的"栅栏柱错误" fencepost error)。
Q3MEDIUMAP MCDe Morgan

Equivalent to !(a < b || c >= d)?与 !(a < b || c >= d) 等价的是?

Answer:答案: (B)
De Morgan: !(P || Q) ≡ !P && !Q. Negate each clause carefully:
  • !(a < b) ≡ a >= b (the negation of "strictly less" is "greater or equal," not "strictly greater")
  • !(c >= d) ≡ c < d
  • Join with &&: a >= b && c < d
Trap (A) drops the equality cases on the first clause. (C) keeps the || connective (forgot the second rule of De Morgan).
德摩根定律(De Morgan's Law):!(P || Q) ≡ !P && !Q。逐个否定每个子条件:
  • !(a < b) ≡ a >= b("严格小于"的否定是"大于等于",不是"严格大于")
  • !(c >= d) ≡ c < d
  • 用 && 连接:a >= b && c < d
陷阱 (A) 漏掉了第一个子条件中的等号情形。(C) 没把 || 换成 &&(忘了德摩根的第二条规则)。
Q4HARDAP MCNested if Trace

x = 15; nested if printing A/B/C, then D always.x = 15;嵌套 if 可能打印 A/B/C,之后总会打印 D。

Answer:答案: (B)
Walk through with x = 15:
  • x > 10 → true, enter outer if. The else if branch is now skipped entirely.
  • x % 2 == 0 → 15 % 2 = 1, so false. The inner else runs: print B.
  • Outside both ifs, println("D") runs: print D + newline.
Final output: BD. Trap (C) assumes else if (x > 5) also fires — but only one branch in an if / else if chain ever runs.
代入 x = 15 推演:
  • x > 10 → true,进入外层 if。else if 分支现在整段被跳过。
  • x % 2 == 0 → 15 % 2 = 1,false。内层 else 执行:打印 B。
  • 跳出两层 if 后,println("D") 执行:打印 D 并换行。
最终输出:BD。陷阱 (C) 认为 else if (x > 5) 也会执行——但在 if / else if 链里,最多只会执行其中一个分支。
Q5HARDAP MCDigit Sum

n = 234; while (n > 0) { s += n % 10; n /= 10; }n = 234;while (n > 0) { s += n % 10; n /= 10; }

Answer:答案: (B)
Standard "extract digits" idiom — % 10 grabs the last digit, /= 10 chops it off.
  • Iteration 1: 234 % 10 = 4 → s = 4; n = 23.
  • Iteration 2: 23 % 10 = 3 → s = 7; n = 2.
  • Iteration 3: 2 % 10 = 2 → s = 9; n = 0.
  • Condition n > 0 fails. Exit. Print 9.
Trap (D) is the reversed-digits answer — see Q9 for that pattern. Different idiom, different answer.
经典的"提取数字"惯用法 —— % 10 取最低位,/= 10 把它去掉。
  • 第 1 轮:234 % 10 = 4 → s = 4;n = 23。
  • 第 2 轮:23 % 10 = 3 → s = 7;n = 2。
  • 第 3 轮:2 % 10 = 2 → s = 9;n = 0。
  • 条件 n > 0 不再满足,退出。打印 9。
陷阱 (D) 是数字反转的答案 —— 见 Q9 的同类模式。不同惯用法,不同答案。
Q6HARDAP MCString Traverse — Case Trap

s = "ProgrAmming"; count chars equal to 'a'/'e'/'i'/'o'/'u'.s = "ProgrAmming";统计等于 'a'/'e'/'i'/'o'/'u' 的字符个数。

Answer:答案: (B)
Comparing chars with == is case-sensitive: 'A' == 'a' is false. Walk through the string:
 P  r  o  g  r  A  m  m  i  n  g
 0  1  2  3  4  5  6  7  8  9 10
Lower-case vowels only: o at 2, i at 8. Count = 2. The uppercase A at index 5 is not matched.

Trap (C) counts the uppercase A by treating the comparison as case-insensitive. To make this counter case-insensitive you'd normalize: Character.toLowerCase(c) == 'a' || …, or call s.toLowerCase() first.

用 == 比较 char 是区分大小写的(case-sensitive):'A' == 'a' 为 false。逐字符走一遍:
 P  r  o  g  r  A  m  m  i  n  g
 0  1  2  3  4  5  6  7  8  9 10
只匹配小写元音:o(下标 2)、i(下标 8)。计数 = 2。下标 5 处的大写 A 不会被匹配。

陷阱 (C) 把大写 A 也算上,相当于把比较当成了不区分大小写。要做成不区分大小写,可以先归一化:Character.toLowerCase(c) == 'a' || …,或先调 s.toLowerCase()。

Q7HARDAP MCwhile — Flag-controlled exit + counter

n=100; count=0; stop=false; while (n>0 && !stop) { count++; if (count>5) stop=true; n-=10; } println(count + " " + n);n=100; count=0; stop=false; while (n>0 && !stop) { count++; if (count>5) stop=true; n-=10; } println(count + " " + n);

Answer:答案: (C)
Two things move inside the body: count increments first; n decrements last. The flag stop is set inside the body, but the loop condition only re-checks after the body completes. So the iteration where count first exceeds 5 still finishes — including the n -= 10.
iter | count after  stop after  n after
  1  |     1         false        90
  2  |     2         false        80
  3  |     3         false        70
  4  |     4         false        60
  5  |     5         false        50
  6  |     6         true         40   ← count>5 fires, stop set, n still drops
loop test now: 40>0 && !true = false → exit
Print: "6 40".

Trap (A) assumes stop=true short-circuits the rest of the body (it doesn't — if/then isn't break). (B) stops after the 5th decrement and before the count increment. (D) ignores the flag entirely.

循环体内有两个变量在变:count 先自增,n 最后再减。标志变量 stop 是在循环体内部被赋值的,但循环条件要等整个循环体执行完才重新检查。所以当 count 第一次超过 5 那一轮,整个循环体仍会跑完——包括 n -= 10。
iter | count after  stop after  n after
  1  |     1         false        90
  2  |     2         false        80
  3  |     3         false        70
  4  |     4         false        60
  5  |     5         false        50
  6  |     6         true         40   ← count>5 触发,stop 置真,但 n 仍然下降
loop test now: 40>0 && !true = false → 退出
打印:"6 40"。

陷阱 (A) 错以为 stop=true 会"短路"掉循环体后续语句(不会——if/then 不是 break)。(B) 在第 5 次减法之后、count 自增之前停止。(D) 完全忽略了标志变量。

Q8HARDAP MCNested Loop Count

for i = 1..4 { for j = 1..i { count++; } }for i = 1..4 { for j = 1..i { count++; } }

Answer:答案: (B)
The inner loop runs i times for each outer i. Total iterations:
  • i = 1: inner runs 1 time
  • i = 2: inner runs 2 times
  • i = 3: inner runs 3 times
  • i = 4: inner runs 4 times
Total = 1 + 2 + 3 + 4 = 10. (This is the triangular number n(n+1)/2.)

Trap (C) assumes a full 4 × 4 rectangle (which would happen if the inner loop were j <= 4 independent of i). (A) counts only outer iterations.

对每个外层 i,内层循环跑 i 次。合计:
  • i = 1:内层跑 1 次
  • i = 2:内层跑 2 次
  • i = 3:内层跑 3 次
  • i = 4:内层跑 4 次
合计 = 1 + 2 + 3 + 4 = 10。(这是三角数,n(n+1)/2。)

陷阱 (C) 把它当成完整的 4 × 4 矩形(如果内层是 j <= 4 与 i 无关,才会是 16)。(A) 只数了外层的次数。

Q9HARDAP MCReverse Digits

n = 1234; while (n > 0) { rev = rev*10 + n%10; n /= 10; }n = 1234;while (n > 0) { rev = rev*10 + n%10; n /= 10; }

Answer:答案: (B)
The recurrence rev = rev * 10 + (last digit of n) "shifts" the previously-accumulated digits left and appends the next digit. Trace:
  • Start: n = 1234, rev = 0
  • Iter 1: rev = 0·10 + 4 = 4; n = 123
  • Iter 2: rev = 4·10 + 3 = 43; n = 12
  • Iter 3: rev = 43·10 + 2 = 432; n = 1
  • Iter 4: rev = 432·10 + 1 = 4321; n = 0 — exit.
Final rev = 4321. Trap (C) stops one iteration early; (D) forgets the + n%10 on the last step.
递推 rev = rev * 10 + (n 的最低位) 把已积累的数字"整体左移一位",然后把新的最低位接到末尾。逐轮推演:
  • 初始:n = 1234, rev = 0
  • 第 1 轮:rev = 0·10 + 4 = 4;n = 123
  • 第 2 轮:rev = 4·10 + 3 = 43;n = 12
  • 第 3 轮:rev = 43·10 + 2 = 432;n = 1
  • 第 4 轮:rev = 432·10 + 1 = 4321;n = 0 —— 退出。
最终 rev = 4321。陷阱 (C) 提前一轮停止;(D) 漏掉了最后一步的 + n%10。
Q10HARDAP MCwhile — OR + guarded decrements

a=5; b=3; while (a>0 || b>0) { if (a>0) a--; if (b>0) b--; } println(a + " " + b);a=5; b=3; while (a>0 || b>0) { if (a>0) a--; if (b>0) b--; } println(a + " " + b);

Answer:答案: (A)
Two subtle things: (1) the loop condition is ||, so the loop runs while either variable is still positive; (2) the inner if guards prevent b from going negative once it hits 0.
iter | a after  b after  loop test after
  1  |    4        2     4>0 || 2>0 → true
  2  |    3        1     true
  3  |    2        0     true
  4  |    1        0     (b guard skipped) true
  5  |    0        0     0>0 || 0>0 → false → exit
Once b = 0, the if (b > 0) guard skips its decrement — so b stays at 0 while a finishes counting down.

Trap (B) drops the if (b>0) guard, so b goes negative (decrements once per iteration past 0). (C) swaps the roles. (D) assumes the loop can't terminate because of the OR — but eventually both reach 0 since each iteration strictly decreases the positive variable(s).

两个细节:(1) 循环条件是 ||,所以只要任何一个变量还为正,循环就继续;(2) 内部的 if 保护(guard)让 b 在到达 0 后不会变成负数。
iter | a after  b after  loop test after
  1  |    4        2     4>0 || 2>0 → true
  2  |    3        1     true
  3  |    2        0     true
  4  |    1        0     (b 的保护跳过自减) true
  5  |    0        0     0>0 || 0>0 → false → 退出
一旦 b = 0,if (b > 0) 保护就会跳过它的自减——所以 b 停在 0,而 a 继续往下数。

陷阱 (B) 没有 if (b>0) 保护,b 会变负(每轮在 0 之后还减一次)。(C) 把两者的角色搞反了。(D) 以为 OR 让循环停不下来——其实只要正值变量每轮严格减少,最终都会到 0。

Q11HARDAP MCwhile Threshold

n = 0; p = 1; while (p < 1000) { p *= 2; n++; }n = 0; p = 1; while (p < 1000) { p *= 2; n++; }

Answer:答案: (B)
We count how many doublings of 1 are needed to reach or exceed 1000.
iter | p after body   n
  1  |   2            1
  2  |   4            2
  3  |   8            3
  4  |  16            4
  5  |  32            5
  6  |  64            6
  7  | 128            7
  8  | 256            8
  9  | 512            9
 10  | 1024          10
loop test: 1024 < 1000 false → exit
Note: the test p < 1000 uses the value of p before the next iteration. p = 512 is still < 1000, so the body runs once more — that final iteration is what pushes n from 9 to 10. Trap (A) stops at p = 512 without doing the final iteration; (D) reports p instead of n.
数一数把 1 翻倍多少次才能达到或超过 1000:
iter | p 执行后   n
  1  |   2        1
  2  |   4        2
  3  |   8        3
  4  |  16        4
  5  |  32        5
  6  |  64        6
  7  | 128        7
  8  | 256        8
  9  | 512        9
 10  | 1024      10
loop test: 1024 < 1000 false → 退出
注意:循环条件 p < 1000 用的是下一次循环开始之前的 p。p = 512 仍然 < 1000,所以循环体还要再跑一次——正是这最后一次把 n 从 9 推到 10。陷阱 (A) 在 p = 512 处停下,没做最后那一轮;(D) 报的是 p 的值而不是 n。
Q12HARDAP MCfor — Integer-division step (log₂ counter)

n=100; count=0; for (int i=n; i>1; i=i/2) count++; println(count);n=100; count=0; for (int i=n; i>1; i=i/2) count++; println(count);

Answer:答案: (B)
The update step i = i / 2 is integer division, so values shrink fast and round down. The condition is i > 1 (not > 0), so the loop exits once i reaches 1.
iter | i before  i after = i/2  count after
  1  |   100        50                1
  2  |    50        25                2
  3  |    25        12                3
  4  |    12         6                4
  5  |     6         3                5
  6  |     3         1                6
loop test: 1 > 1 false → exit
So count = 6 — equivalently, ⌊log₂(100)⌋ = 6 (since 2⁶ = 64 ≤ 100 < 128 = 2⁷).

Trap (A) stops when i reaches 2 (off-by-one on the loop bound). (C) counts the would-be iteration at i = 1. (D) mistakes the loop for one that runs n/2 times.

更新步 i = i / 2 是整除,所以值缩小很快、还会向下取整。退出条件是 i > 1(不是 > 0),所以 i 到 1 就退出。
iter | i 执行前   i 执行后 = i/2  count 执行后
  1  |   100        50                1
  2  |    50        25                2
  3  |    25        12                3
  4  |    12         6                4
  5  |     6         3                5
  6  |     3         1                6
loop test: 1 > 1 false → 退出
所以 count = 6 —— 等价于 ⌊log₂(100)⌋ = 6(因为 2⁶ = 64 ≤ 100 < 128 = 2⁷)。

陷阱 (A) 在 i 到 2 时就停(循环边界算错了一)。(C) 多算了 i = 1 时那一轮。(D) 把循环误当作要跑 n/2 次。

Q13HARDAP MCif-else-if chain vs independent ifs

x=7; result=""; if(x>0) result+="P"; if(x%2==0) result+="E"; else if(x>5) result+="B"; if(x<10 && x>3) result+="M"; println(result);x=7; result=""; if(x>0) result+="P"; if(x%2==0) result+="E"; else if(x>5) result+="B"; if(x<10 && x>3) result+="M"; println(result);

Answer:答案: (A)
The structure is three separate if statements — but the second one has an attached else if. That changes its behavior: the chain runs at most one of its branches; the independent ifs each run on their own merits.
  • 1st if (independent): x > 0 → 7 > 0 true → append "P". Result: "P".
  • 2nd if/else if (chain): x % 2 == 0 → 7 % 2 = 1 false → check else if (x > 5) → true → append "B". The "E" branch is skipped and so is any sibling once one branch fires. Result: "PB".
  • 3rd if (independent): x < 10 && x > 3 → both true → append "M". Result: "PBM".
Print: "PBM".

Trap (B) "PEBM" treats the chain as two independent ifs (both E and B branches appended). (C) ignores the final independent if. (D) sees the chain correctly but skips both its branches (forgetting else if).

结构是三条独立的 if 语句——但第二条带了一个 else if。这就改变了它的行为:链式结构里最多只执行一个分支;独立的 if 各自按条件独立判断。
  • 第 1 条 if(独立):x > 0 → 7 > 0 true → 追加 "P"。结果:"P"。
  • 第 2 条 if/else if(链式):x % 2 == 0 → 7 % 2 = 1 false → 检查 else if (x > 5) → true → 追加 "B"。"E" 分支被跳过——同一个链里一旦有分支命中,剩下的分支都不会运行。结果:"PB"。
  • 第 3 条 if(独立):x < 10 && x > 3 → 两个都为 true → 追加 "M"。结果:"PBM"。
打印:"PBM"。

陷阱 (B) "PEBM" 把链式当作两个独立 if(E 和 B 两个分支都追加)。(C) 忽略了最后那条独立 if。(D) 链式判断正确但跳过了两个分支(忘了 else if)。

Q14HARDAP MCNested loop — modular pair counting

total=0; for(i=1..6) for(j=i..6) if((i+j)%3==0) total++; println(total);total=0; for(i=1..6) for(j=i..6) if((i+j)%3==0) total++; println(total);

Answer:答案: (B)
The inner loop starts at j = i (not 1), so we only count unordered pairs (i, j) with i ≤ j ≤ 6 where i + j is divisible by 3. Walk row by row:
i=1, j∈{1..6}: hits at j=2 (sum 3), j=5 (sum 6)             → 2
i=2, j∈{2..6}: hits at j=4 (sum 6)                          → 1
i=3, j∈{3..6}: hits at j=3 (sum 6), j=6 (sum 9)             → 2
i=4, j∈{4..6}: hits at j=5 (sum 9)                          → 1
i=5, j∈{5..6}: no hits (sums 10, 11)                        → 0
i=6, j=6:      hits at j=6 (sum 12)                         → 1
                                                  total = 2+1+2+1+0+1 = 7
Print: 7.

Trap (D) 12 counts the full 6×6 grid's hits (would be right if inner ran j = 1..6 — double-counts unordered pairs and includes ordered duplicates). (A) 6 miscounts a row. (C) 8 double-counts a single pair.

内层循环从 j = i 而不是 1 开始,所以只数无序对 (i, j)(满足 i ≤ j ≤ 6)且 i + j 能被 3 整除的情况。逐行扫描:
i=1, j∈{1..6}: 命中 j=2(和 3)、j=5(和 6)            → 2
i=2, j∈{2..6}: 命中 j=4(和 6)                          → 1
i=3, j∈{3..6}: 命中 j=3(和 6)、j=6(和 9)             → 2
i=4, j∈{4..6}: 命中 j=5(和 9)                          → 1
i=5, j∈{5..6}: 没有命中(和为 10、11)                   → 0
i=6, j=6:      命中 j=6(和 12)                          → 1
                                                  total = 2+1+2+1+0+1 = 7
打印:7。

陷阱 (D) 12 是把整个 6×6 网格的命中都数上(相当于内层跑 j = 1..6——把无序对重复计数)。(A) 6 某一行数错了。(C) 8 多算了一对。

Q15HARDAP MCwhile — Conditional shrink (halve/decrement)

n=13; count=0; while(n>1) { if(n%2==0) n/=2; else n--; count++; } println(count);n=13; count=0; while(n>1) { if(n%2==0) n/=2; else n--; count++; } println(count);

Answer:答案: (B)
Each iteration either halves n (when even) or subtracts 1 (when odd). count tracks every iteration, not just halvings. Trace from n = 13:
iter | n before  branch   n after  count after
  1  |    13      else      12          1     (odd: decrement)
  2  |    12      if         6          2     (even: halve)
  3  |     6      if         3          3     (even: halve)
  4  |     3      else       2          4     (odd: decrement)
  5  |     2      if         1          5     (even: halve)
loop test: 1 > 1 false → exit
Print: 5.

Trap (A) 4 miscounts by 1 — usually skipping the first "odd → 12" iteration. (C) 6 runs one extra loop (forgets the exit is at n = 1, not 0). (D) 12 counts the value of n after one decrement instead of count.

每轮要么把 n 减半(偶数时),要么减 1(奇数时)。count 记录的是每一轮的次数,而不仅仅是减半的次数。从 n = 13 推演:
iter | n 执行前   分支     n 执行后   count 执行后
  1  |    13      else        12          1     (奇数:减 1)
  2  |    12      if           6          2     (偶数:减半)
  3  |     6      if           3          3     (偶数:减半)
  4  |     3      else         2          4     (奇数:减 1)
  5  |     2      if           1          5     (偶数:减半)
loop test: 1 > 1 false → 退出
打印:5。

陷阱 (A) 4 数错了一次——通常是漏掉第一轮"奇数 → 12"。(C) 6 多跑了一轮(忘了退出条件是 n = 1,不是 0)。(D) 12 把一次减 1 后的 n 当作了答案,没看 count。

FREE RESPONSEReference Solution & Rubric参考答案与评分标准

Free-Response Question — Reference Solution自由回答题 — 参考解答

FRQ 1HARD METHODS & CONTROL STRUCTURES 2.3 / 2.5 / 2.8 / 2.9 [7 pts]

Greenhouse vent controller. Part (a): write ventLevel(int h), returning the vent level for hour h from the temperature recorded that hour (below 18 → 0; 18–23 → 1; 24–29 → 2; 30 or above → 3). Part (b): write ventName(String label), returning the portion of label before its first #, or the whole label when it contains no #.温室通风控制器。(a) 小题:编写 ventLevel(int h),由第 h 小时记录到的温度返回该小时的通风档位(低于 18 → 0;18–23 → 1;24–29 → 2;30 及以上 → 3)。(b) 小题:编写 ventName(String label),返回 label 中第一个 # 之前的部分;若其中不含 #,则返回整个标签。

Reference solution:参考答案:  (a) a four-branch if/else if chain on tempAt(h)对 tempAt(h) 作四分支 if/else if 判断  ·  (b) indexOf to locate the first #, then substring — with the not-found case handled first用 indexOf 定位第一个 #,再用 substring 截取 — 并优先处理「未找到」的情形

(a) ventLevel — ordered threshold tests(a) ventLevel — 有序阈值判断

Read the temperature once, then test the thresholds in increasing order. Because an else if chain is only reached when every earlier test failed, each branch needs only its upper bound — the lower bound is already guaranteed.先读取一次温度,再按由小到大的顺序检查各阈值。由于 else if 链中的某一分支只有在前面所有判断都为假时才会到达,因此每个分支只需写出上界,下界已被前面的判断所保证。

public static int ventLevel(int h)
{
    int t = tempAt(h);
    if (t < 18)
    {
        return 0;
    }
    else if (t < 24)
    {
        return 1;
    }
    else if (t < 30)
    {
        return 2;
    }
    else
    {
        return 3;
    }
}

The four bands are contiguous and cover every int, so a final bare else is correct: any temperature that reaches it is 30 or above. Negative temperatures fall into the first branch, as required.四个区间彼此相连并覆盖全部 int 取值,因此最后使用不带条件的 else 是正确的:能到达该分支的温度必然不小于 30。负温度会落入第一个分支,符合题目要求。

(b) ventName — locate, then shorten(b) ventName — 先定位,再截取

indexOf answers two questions at once: is the character there, and where. It returns the position of the first occurrence, or -1 if there is none. Ask the -1 question first, so that by the time substring runs the position is known to be valid.indexOf 一次回答两个问题:该字符是否存在,以及它在哪里。它返回首次出现的位置,若不存在则返回 -1。应先判断 -1 的情形,这样在执行 substring 时位置必然已是合法的。

public static String ventName(String label)
{
    int pos = label.indexOf("#");
    if (pos == -1)
    {
        return label;
    }
    return label.substring(0, pos);
}

substring(0, pos) takes the characters from index 0 up to but not including index pos. Because the end index is exclusive, the located # is excluded automatically — no - 1 is needed anywhere. The exclusive end is also what makes ventName("#auto") work: pos is 0, so substring(0, 0) returns the empty string, which is the right answer.substring(0, pos) 截取从下标 0 起、直到下标 pos 但不包含 pos 的那些字符。由于结束下标是开区间(不含),被定位到的 # 会被自动排除 — 任何地方都不需要写 - 1。正是这个开区间使 ventName("#auto") 得到正确结果:此时 pos 为 0,substring(0, 0) 返回空字符串,恰为所求。

Because indexOf returns the first occurrence, a label with several # characters needs no extra handling: ventName("ridge#manual#2") already returns "ridge".由于 indexOf 返回的是首次出现的位置,含有多个 # 的标签无需任何额外处理:ventName("ridge#manual#2") 本来就会返回 "ridge"。

Analytic rubric — 7 points, one per criterion (4 + 3).分项评分标准 — 共 7 分,每个评分点 1 分(4 + 3)。
#序号Criterion — awarded for observable behavior评分点 — 依据可观察到的行为给分Pts分值
A1Calls tempAt(h) and uses the value it returns调用 tempAt(h) 并使用其返回值1
A2Tests the temperature against the boundary values 18, 24, and 30, placing each boundary temperature itself in the higher band以 18、24、30 为边界进行判断,且使每个边界温度本身落入较高的那个档位1
A3Produces all four levels 0, 1, 2, 3, each for its own band能产生 0、1、2、3 四个档位,且各自对应正确区间1
A4Returns an int on every path, including temperatures below 18 and at or above 30在所有执行路径上都返回一个 int,包括低于 18 与不低于 30 的情形1
B1Locates the first # in label and stores or uses that position定位 label 中第一个 # 的位置,并保存或使用该位置1
B2Returns label itself when label contains no #当 label 中不含 # 时,返回 label 本身1
B3When a # is present, returns exactly the characters before it — the # itself is not included and no earlier character is dropped当存在 # 时,恰好返回其之前的那些字符 — 既不包含 # 本身,也不漏掉它之前的任何字符1
 Total合计7
Any implementation meeting a criterion earns it. Part (a) may use nested if statements, descending thresholds, or a single return variable instead of the chain shown. In part (b), indexOf may be called with a String or a char argument, the not-found test may be written pos < 0, and a response that loops character by character to find the first # earns B1 provided it stops at the first one. B2 and B3 are judged independently: a response that handles only one of the two cases earns the criterion for the case it handles.任何满足某评分点的实现均可得该分。(a) 小题可以使用嵌套 if、按降序排列阈值,或用一个返回变量代替上述链式结构。(b) 小题中,indexOf 的实参可以是 String 也可以是 char;「未找到」的判断写成 pos < 0 同样可以;若逐字符循环查找第一个 #,只要在首次出现处停止即可得 B1。B2 与 B3 独立评判:只正确处理了其中一种情形的作答,可得该情形所对应的评分点。
Common traps.常见陷阱。
  • Overlapping or gapped bands in (a). Writing if (t <= 18) pushes 18 into level 0; writing if (t > 18 && t < 24) as an independent if leaves 18 itself unhandled. The band edges are 18, 24, 30 and each edge belongs to the higher band.(a) 中区间重叠或留有空隙。写成 if (t <= 18) 会把 18 归入档位 0;把 if (t > 18 && t < 24) 写成独立的 if 则会使 18 本身无人处理。区间边界为 18、24、30,且每个边界值都属于较高的那个档位。
  • Assuming the # is always there. Calling substring(0, pos) without first testing pos throws StringIndexOutOfBoundsException on a label such as "ridge", because pos is -1. Every indexOf call has two outcomes, and the -1 one is the one that is forgotten.想当然地认为 # 一定存在。未先判断 pos 就调用 substring(0, pos),在遇到 "ridge" 这类标签时会抛出 StringIndexOutOfBoundsException,因为此时 pos 为 -1。每次 indexOf 调用都有两种结果,而被遗忘的总是 -1 那一种。
  • Off-by-one at the cut. substring(0, pos + 1) keeps the #; substring(0, pos - 1) drops the last character of the name. The second index of substring is exclusive, so pos alone is already correct.截取位置差一。substring(0, pos + 1) 会把 # 一并保留;substring(0, pos - 1) 则会丢掉名称的最后一个字符。substring 的第二个下标是不包含的,因此直接用 pos 本身就已正确。
  • Returning the wrong side of the cut. substring(pos) and substring(pos + 1) both return the mode suffix, not the display name.返回了截取点的另一侧。substring(pos) 与 substring(pos + 1) 返回的都是模式后缀,而非显示名称。
  • Using lastIndexOf. On "ridge#manual#2" it finds the second # and returns "ridge#manual". The specification names the first occurrence, which is what indexOf gives.误用 lastIndexOf。对 "ridge#manual#2",它找到的是第二个 #,从而返回 "ridge#manual"。题目规定的是首次出现的位置,这正是 indexOf 所返回的。
Insight.要点。 Part (b) is the whole String-FRQ pattern in miniature: locate, then shorten. The two halves fail in different ways, which is why they are scored separately. Locating fails on the sentinel — indexOf does not return a position when there is nothing to find, it returns -1, and code that treats -1 as an index crashes rather than misbehaving quietly. Shortening fails on the boundary — and the fix is not arithmetic but a fact about the method: substring’s end index is exclusive, so the index you found is already the index you cut at. Internalize the half-open range and the + 1 / - 1 guessing that costs so many String points simply stops.(b) 小题是字符串类自由回答题模式的缩影:先定位,再截取。这两半以不同的方式出错,故需分别评分。定位环节栽在「哨兵值」上 — 当无可查找之物时,indexOf 并不会返回某个位置,而是返回 -1;把 -1 当作下标使用的代码会直接崩溃,而不是悄悄给出错误结果。截取环节栽在「边界」上 — 而其解法并非算术调整,而是关于方法本身的一个事实:substring 的结束下标是不包含的,因此你找到的那个下标本身就是应当下刀的位置。真正理解了这一「左闭右开」区间,那种在 + 1 与 - 1 之间反复猜测、白白丢掉字符串题分数的情况便会自然消失。