---
# System prepended metadata

title: 第五章 述句 練習

---

# 第五章 述句 練習

## 練習 5.1

null statement 是什麼？什麼時候你會用到一個 null statement？

解：

只含一個單獨的分號的述句就是 null statement，如果在程式的某個地方，語法上需要一條述句但是邏輯上不需要，此時就應該使用 null statement

```cpp
while (cin >> s && s != sought);
```

## 練習 5.2

區塊是什麼？什麼時候你會用到一個區塊？

解：

用大括號（curly brackets）包起來的述句和宣告的序列就是區塊，如果在程式的某個地方，語法上需要一條述句，而邏輯上需要多條述句，此時應該使用區塊

```cpp
while (val <= 10) {
  sum += val;
  ++val;
}
```

## 練習 5.3

使用逗號運算子（comma operator）改寫來自 1.4.1 的 while 迴圈，讓它不再需要一個區塊。解釋這種改寫方式增進或減少程式碼的可讀性

解：

```cpp
while (val <= 10)
  sum += val, ++val;
```
程式碼的可讀性反而降低了

## 練習 5.4

解釋下列每個範例，更正你所發現的任何問題

```cpp
（a）while(string::iterator iter != s.end()) { /* . . . */
     }
（b）while(bool status = find(word)) { /* . . . */
     }
if (!status) { /* . . . */
     }
```

解：

- （a）這個迴圈試圖用迭代器遍歷 string，但是變數的定義應該放在迴圈的外面，而目前的迴圈每次都會重新定義一個變數，因此是錯誤
- （b）這個迴圈的 while 和 if 是兩個獨立的述句，if 述句中無法存取 status 變數，正確的作法是應該將 if 包含在 while 裡面

## 練習 5.5

使用一個 if else 述句寫出你自己版本的程式，來從數值成績產生字母成績

解：

```cpp
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main() {
  vector<string> scores = {"F", "D", "C", "B", "A", "A++"};
  for (int g; cin >> g;) {
    string letter;
    if (g < 60)
      letter = scores[0];
    else {
      letter = scores[(g - 50) / 10];
      if (g != 100)
        letter += g % 10 > 7 ? "+" : g % 10 < 3 ? "-" : "";
    }
    cout << letter << endl;
  }
  return 0;
}
```

## 練習 5.6

改寫你的成績程式，使用條件運算子（conditional operator），來取代 if else 述句

解：


```cpp
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main() {
  vector<string> scores = {"F", "D", "C", "B", "A", "A++"};
  int grade = 0;
  while (cin >> grade) {
    string lettergrade = grade < 60 ? scores[0] : scores[(grade - 50) / 10];
    lettergrade += (grade == 100 || grade < 60) ? ""
                   : (grade % 10 > 7)           ? "+"
                   : (grade % 10 < 3)           ? "-"
                                                : "";
    cout << lettergrade << endl;
  }
  return 0;
}
```

## 練習 5.7

更正下列程式碼片段中的錯誤：

```cpp
（a）if (ival1 != ival2)
		 ival1 = ival2
     else
    	 ival1 = ival2 = 0;
（b）if (ival < minval)
		minval = ival;
    	occurs = 1;
（c）if (int ival = get_value())
    	cout << "ival = " << ival << endl;
     if (!ival)
    	cout << "ival = 0\n";
（d）if (ival = 0)
    	ival = get_value();
```

解：

- （a）ival1 = ival2 後面少了分號（semi-colon）
- （b）應使用花括號包起來（curly braces）
- （c）if (!ival) 應改為 else
- （d）if (ival = 0) 應改為 if (ival == 0)

## 練習 5.8

什麼是懸置的 else（dangling else）？C++ 中的 else 子句如何解析？

解：

用來描述在嵌套的 if else 述句中，如果 if 比 else 多時如何處理的問題。C++ 使用的方法是 else 配對最接近的 if，若沒有配對到即為懸置 else

## 練習 5.9

使用一系列的 if 述句寫一個程式計數從 cin 讀入的文字中母音的總數

解：

```cpp
#include <iostream>

using std::cin;
using std::cout;
using std::endl;

int main() {
  unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
  char ch;
  while (cin >> ch) {
    if (ch == 'a')
      ++aCnt;
    else if (ch == 'e')
      ++eCnt;
    else if (ch == 'i')
      ++iCnt;
    else if (ch == 'o')
      ++oCnt;
    else if (ch == 'u')
      ++uCnt;
  }
  cout << "Number of vowel a:\t" << aCnt << '\n'
       << "Number of vowel e:\t" << eCnt << '\n'
       << "Number of vowel i:\t" << iCnt << '\n'
       << "Number of vowel o:\t" << oCnt << '\n'
       << "Number of vowel u:\t" << uCnt << endl;
  return 0;
```

## 練習 5.10

我們實作的母音計數程式有一個問題：它不會把大寫字母算為母音。寫個能夠將大小寫字母都適切地算為母音的程式，也就是說，你的程式應該將 'a' 及 'A' 都累計到 aCnt，依此類推

解：

```cpp
#include <iostream>

using std::cin;
using std::cout;
using std::endl;

int main() {
  unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
  char ch;
  while (cin >> ch) {
    ch = tolower(ch);
    if (ch == 'a')
      ++aCnt;
    else if (ch == 'e')
      ++eCnt;
    else if (ch == 'i')
      ++iCnt;
    else if (ch == 'o')
      ++oCnt;
    else if (ch == 'u')
      ++uCnt;
  }
  cout << "Number of vowel a:\t" << aCnt << '\n'
       << "Number of vowel e:\t" << eCnt << '\n'
       << "Number of vowel i:\t" << iCnt << '\n'
       << "Number of vowel o:\t" << oCnt << '\n'
       << "Number of vowel u:\t" << uCnt << endl;
  return 0;
}
```

## 練習 5.11

修改我們的母音計數程式，讓它也計數讀到的空格、tab 以及 newlines

解：

```cpp
#include <iostream>

using std::cin;
using std::cout;
using std::endl;
using std::noskipws;

int main() {
  unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0,
           tabCnt = 0, newLineCnt = 0;
  char ch;
  while (cin >> noskipws >> ch) { // no skip whitespce
    ch = tolower(ch);
    if (ch == 'a')
      ++aCnt;
    else if (ch == 'e')
      ++eCnt;
    else if (ch == 'i')
      ++iCnt;
    else if (ch == 'o')
      ++oCnt;
    else if (ch == 'u')
      ++uCnt;
    else if (ch == ' ')
      ++spaceCnt;
    else if (ch == '\t')
      ++tabCnt;
    else if (ch == '\n')
      ++newLineCnt;
  }
  cout << "Number of vowel a: \t" << aCnt << '\n'
       << "Number of vowel e: \t" << eCnt << '\n'
       << "Number of vowel i: \t" << iCnt << '\n'
       << "Number of vowel o: \t" << oCnt << '\n'
       << "Number of vowel u: \t" << uCnt << '\n'
       << "Number of space: \t" << spaceCnt << '\n'
       << "Number of tab char: \t" << tabCnt << '\n'
       << "Number of new line: \t" << newLineCnt << endl;
  return 0;
}
```

使用 noskipws 可以保留默認跳過的空格

## 練習 5.12

修改我們的母音計數程式，讓它計數下列雙字母序列出現的頻率：ff、fl 與 fi

解：

```cpp
#include <iostream>

using std::cin;
using std::cout;
using std::endl;
using std::noskipws

int main() {
  unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0, spaceCnt = 0,
           tabCnt = 0, newLineCnt = 0;
  unsigned ffCnt = 0, flCnt = 0, fiCnt = 0;
  char cur, prev = '\0';
  while (cin >> noskipws >> cur) { // no skip whitespce
    cur = tolower(cur);
    if (cur == 'a')
      ++aCnt;
    else if (cur == 'e')
      ++eCnt;
    else if (cur == 'i') {
      ++iCnt;
      if (prev == 'f')
        fiCnt++;
    } else if (cur == 'o')
      ++oCnt;
    else if (cur == 'u')
      ++uCnt;
    else if (cur == ' ')
      ++spaceCnt;
    else if (cur == '\t')
      ++tabCnt;
    else if (cur == '\n')
      ++newLineCnt;
    else if (cur == 'l' && prev == 'f')
      flCnt++;
    else if (cur == 'f' && prev == 'f')
      ffCnt++;
    prev = cur;
  }
  cout << "Number of vowel a: \t" << aCnt << '\n'
       << "Number of vowel e: \t" << eCnt << '\n'
       << "Number of vowel i: \t" << iCnt << '\n'
       << "Number of vowel o: \t" << oCnt << '\n'
       << "Number of vowel u: \t" << uCnt << '\n'
       << "Number of space: \t" << spaceCnt << '\n'
       << "Number of tab char: \t" << tabCnt << '\n'
       << "Number of new line: \t" << newLineCnt << '\n'
       << "Number of ff: \t" << ffCnt << '\n'
       << "Number of fl: \t" << flCnt << '\n'
       << "Number of fi: \t" << fiCnt << endl;
  return 0;
}
```

## 練習 5.13

列於後面用於練習 5.13 的程式碼中的每個程式都含有一個常見的程式設計錯誤。請找出並更正每個錯誤

```cpp
（a）unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
     char ch = next_text();
     switch (ch) {
         case 'a': aCnt++;
         case 'e': eCnt++;
         default: iouCnt++;
     }
（b）unsigned index = some_value();
     switch (index) {
         case 1:
             int ix = get_value();
             ivec[ ix ] = index;
             break;
         default:
             ix = ivec.size()-1;
             ivec[ ix ] = index;
     }
（c）unsigned evenCnt = 0, oddCnt = 0;
     int digit = get_num() % 10;
     switch (digit) {
         case 1, 3, 5, 7, 9:
             oddcnt++;
             break;
         case 2, 4, 6, 8, 10:
             evencnt++;
             break;
     }
（d）unsigned ival=512, jval=1024, kval=4096;
     unsigned bufsize;
     unsigned swt = get_bufCnt();
     switch(swt) {
         case ival:
             bufsize = ival * sizeof(int);
             break;
         case jval:
             bufsize = jval * sizeof(int);
             break;
         case kval:
             bufsize = kval * sizeof(int);
             break;
     }
```

解：

- （a）每個 case 對應的述句後應該再加上 break

```cpp
unsigned aCnt = 0, eCnt = 0, iouCnt = 0;
char ch = next_text();
switch (ch) {
case 'a':
  aCnt++;
  break;
case 'e':
  eCnt++;
  break;
default:
  iouCnt++;
  break;
}
```

- （b）在 default 分支當中，ix 並未定義，應該在 switch 的外部定義 ix

```cpp
unsigned index = some_value();
int ix;
switch (index) {
case 1:
  ix = get_value();
  ivec[ix] = index;
  break;
default:
  ix = static_cast<int>(ivec.size()) - 1;
  ivec[ix] = index;
}
```

- （c）case 後面使用冒號而不是逗號

```cpp
unsigned evenCnt = 0, oddCnt = 0;
int digit = get_num() % 10;
switch (digit) {
case 1:
case 3:
case 5:
case 7:
case 9:
  oddcnt++;
  break;
case 2:
case 4:
case 6:
case 8:
case 0:
  evencnt++;
  break;
}
```

- （d）case lable 必須要是整數型別的常數運算式（constant expression）

```cpp
const unsigned ival = 512, jval = 1024, kval = 4096;
unsigned bufsize;
unsigned swt = get_bufCnt();
switch (swt) {
case ival:
  bufsize = ival * sizeof(int);
  break;
case jval:
  bufsize = jval * sizeof(int);
  break;
case kval:
  bufsize = kval * sizeof(int);
  break;
}
```

## 練習 5.14

寫個程式從標準輸入讀取 string，尋找重複的字詞。這個程式應該在輸入中找尋一個字詞後緊接著自身的地方。紀錄重複最多的次數，以及重複的是哪個字。印出次數最多的重複，或印出一個訊息表示沒有任何字詞重複。舉例來說，如果輸入是：how now now now brown cow cow，那麼輸出就應該指出 now 這個字出現了三次

解：

```cpp
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::pair;
using std::string;

int main() {
  pair<string, int> maxDuplicated;
  int Cnt = 0;
  for (string cur, prev; cin >> cur; prev = cur) {
    if (cur == prev)
      ++Cnt;
    else {
      Cnt = 0;
      continue;
    }
    if (Cnt > maxDuplicated.second)
      maxDuplicated = {prev, Cnt};
  }
  if (maxDuplicated.first.empty())
    cout << "There's no duplicated string." << endl;
  else
    cout << "the word " << maxDuplicated.first << " occurred "
         << maxDuplicated.second + 1 << " times. " << endl;
  return 0;
}
```

## 練習 5.15

解釋下列每個迴圈。更正你所偵測到的任何問題

```cpp
（a）for (int ix = 0; ix != sz; ++ix) { /* ... */ }
     if (ix != sz)
       	// . . .
（b）int ix;
     for (ix != sz; ++ix) { /* ... */ }
（c）for (int ix = 0; ix != sz; ++ix, ++sz) { /*...*/ }
```

解：

- （a）ix 所在的範疇太小，需在外部宣告

```cpp
int ix;
for (ix = 0; ix != sz; ++ix) { /* ... */
}
if (ix != sz)
// . . .
```
- （b）for 標頭必須要有三個欄位，缺一不可

```cpp
int ix;
for (; ix != sz; ++ix) { /* ... */
}
```
- （c）此迴圈可能為無限迴圈
```cpp
for (int ix = 0; ix != sz; ++ix) { /*...*/
}
```

## 練習 5.16

while 迴圈特別適合用於某個條件成立時就要重複執行的工作，例如當我們需要不斷讀取值，值到檔案結尾為止的時候。for 迴圈一般被想成是一種逐步迴圈（step loop）：藉由一個索引逐步處理過某個群集（collection）中一整個範圍（range）的值。以這兩個迴圈各自的習慣寫出兩個程式使用它們，然後改以另一個迴圈構造改寫這兩個程式。如果你只能使用一種迴圈，你會選哪個？為什麼呢？

解：

```cpp
int i;
while ( cin >> i )
    // ...

int ary[10];
for (int i = 0; i <= 10; i++){
    ary[i]; // ...
}
```
```cpp
for (int i = 0; cin >> i;)
    // ...
int ary[10];
int i = 0;
while (i != 10)
{
    // ...
    ++i;
}
```

如果只能用一種迴圈，我傾向使用 while，因為 while 顯得更簡潔，程式碼可讀性更強

## 練習 5.17

給定由 int 組成的兩個 vector，寫一個程式來判斷其中一個 vector 是否為另一個的前綴（prefix）。對於長度不同的 vector，比較較小的 vector 的元素數。舉例來說，若給定分別含有 0、1、1 與 2 以及 0、1、1、2、3、5、8 的兩個 vector，你的程式應該回傳 true

解：

```cpp
#include <iostream>
#include <vector>

using std::cout;
using std::vector;

bool is_prefix(vector<int> const &lhs, vector<int> const &rhs) {
  if (lhs.size() > rhs.size())
    return is_prefix(rhs, lhs);
  for (unsigned i = 0; i != lhs.size(); ++i)
    if (lhs[i] != rhs[i])
      return false;
  return true;
}

int main() {
  vector<int> v1{0, 1, 1, 2};
  vector<int> v2{0, 1, 1, 2, 3, 5, 8};
  cout << (is_prefix(v2, v1) ? "yes\n" : "no\n");
  return 0;
}
```

## 練習 5.18

解釋下列的迴圈。更正你所偵測到的任何問題

```cpp
（a）do
        int v1, v2;
        cout << "Please enter two numbers to sum:" ;
        if (cin >> v1 >> v2)
            cout << "Sum is: " << v1 + v2 << endl;
    while (cin);
（b）
    do {
    } while (int ival = get_response());
（c）
    do {
       int ival = get_response();
    } while (ival);
```
解：

- （a）do while 之間應加上大括號（curly braces）
- （b）應將 ival 定義在 do while 之外
- （c）應將 ival 定義在 do while 之外


## 練習 5.19

寫一個程式，使用 do while 向使用者重複請求兩個 string，並回報哪個 string 比較短

解：

```cpp
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::string;

int main() {
  string nextCheck;
  do {
    cout << "Input two strings: ";
    string str1, str2;
    cin >> str1 >> str2;
    cout << (str1 == str2  ? "Equal"
                  : str1 < str2 ? "First small"
                                : "Second small")
              << endl;
    cout << " Try again? Enter y or n";
    cin >> nextCheck;
  } while (!nextCheck.empty() && tolower(nextCheck[0]) == 'y');
  return 0;
}
```

## 練習 5.20

寫一個程式從標準輸入讀取一序列的 string，直到相同的字詞（word）連續出現兩次，或所有的字都已讀完為止。使用一個 while 迴圈從文字輸入一次讀取一個字。使用 break 述句在某個字連續出現兩次時，終結迴圈。如果是出現兩次，就印出那個字，不然就印出一個訊息指出沒有重複的字

解：

```cpp
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::string;

int main() {
  string cur, prev;
  bool twice = false;
  while (cin >> cur) {
    if (cur == prev) {
      twice = true;
      break;
    }
    prev = cur;
  }
  if (twice)
    cout << cur << " occurs twice in succession." << endl;
  else
    cout << "no word was repeated." << endl;
  return 0;
}
```

## 練習 5.21

修改 5.5.1 中的練習，讓它僅尋找以一個大寫字母開頭的重複字詞

解：

```cpp
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::string;

int main() {
  string cur, prev;
  bool twice = false;
  while (cin >> cur) {
    if (isupper(cur.at(0)) && cur == prev) {
      twice = true;
      break;
    }
    prev = cur;
  }
  if (twice)
    cout << cur << " occurs twice in succession." << endl;
  else
    cout << "No word was repeated." << endl;
  return 0;
}
```

## 練習 5.22

本節最後一個會跳回 begin 的那個範例用一個迴圈來寫會比較好。請改寫那段程式碼，消除 goto 的使用

```cpp
// 往回跳過一個已經初始化過的變數定義是沒問題的
begin : int sz = get_size();
if (sz <= 0) {
  goto begin;
}
```

解：

可使用 for、while 迴圈修改

```cpp
// for loop
for (int sz = get_size(); sz <= 0; sz = get_size())
  ;
```

``` cpp
// while loop
int sz = get_size();
while (sz <= 0)
  sz = get_size();
```

## 練習 5.23

寫一個程式從標準輸入讀取兩個整數，並印出第一個數字除以第二個數字的結果

解：

```cpp
#include <iostream>

using std::cin;
using std::cout;
using std::endl;

int main() {
  int i, j;
  cin >> i >> j;
  cout << i / j << endl;
  return 0;
}
```

## 練習 5.24

改寫你的程式，讓它在第二個數字是零的時候擲出一個例外。以一個零輸入測試你的程式，看看如果你不 catch 例外，在你的系統上會發生甚麼事

解：

```cpp
#include <iostream>
#include <stdexcept>

using std::cin;
using std::cout;
using std::endl;
using std::runtime_error;

int main(void) {
  int i, j;
  cin >> i >> j;
  if (j == 0)
    throw runtime_error("Divisor is 0.");
  cout << i / j << endl;
  return 0;
}
```

如果沒有使用 catch 抓住 exception，會導致程式呼叫 terminate 的程式庫中的某個函式，依據系統而定，在 windows 中會呼叫 abort() 而使該程式結束執行

## 練習 5.25

改寫前一個練習你寫的程式，使用一個 try 區塊來 catch 那個例外。這個 catch 子句應該印出一個訊息給使用者，並要求他們提供一個新的數字，並重複 try 裡面的程式碼

解：

```cpp
#include <iostream>
#include <stdexcept>

using std::cin;
using std::cout;
using std::endl;
using std::runtime_error;

int main(void) {
  for (int i, j; cout << "Input two integers:\n", cin >> i >> j;) {
    try {
      if (j == 0)
        throw runtime_error("Divisor is 0.");
      cout << i / j << endl;
    } catch (runtime_error err) {
      cout << err.what() << "\tTry again!" << endl;
    }
  }
  return 0;
}
```