###### tags: `CPP` `Arduino`
# 05 遞增運算與 for 迴圈--設計呼吸燈
在前一個教學中, 我們利用 `count = count + 1` 這樣的寫法來增加切換次數, 這種為變數增加數值的寫法很常用到, 因此在 C++ 中提供有一個簡便的寫法, 可以寫成 **`count += 1`**, 若是寫 `count += 3` 就等同於 `count = count + 3`。如果要加的數是 1, 還有另一種更簡單的寫法, 寫成 **`count++`**。
:::info
類似的運算有:
|運算|範例|對等的結果|運算|範例|對等的運算|
|---|---|---|---|---|---|
|[+=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compoundaddition/)| a += b | a = a + b|[-=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compoundsubtraction/)| a -= b | a = a - b|
|[\*=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compoundmultiplication/)| a \*= b | a = a \* b|[/=](https://www.arduino.cc/reference/en/language/structure/compound-operators/compounddivision/)| a /= b | a = a / b|
|[++](https://www.arduino.cc/reference/en/language/structure/compound-operators/increment/)| a++ | a = a + 1|[\-\-](https://www.arduino.cc/reference/en/language/structure/compound-operators/decrement/)| a\-\- | a = a - 1|
:::
我們可以將剛剛的程式改寫如下:
```cpp=
int duration_on = 0; // 亮燈時間
int count = 0; // 計算測換次數
void setup() {
pinMode(5, OUTPUT);
}
void loop() {
if(count == 100) {
duration_on = (duration_on + 1) % 101;
count = 0;
}
digitalWrite(5, LOW);
delayMicroseconds(duration_on);
digitalWrite(5, HIGH);
delayMicroseconds(100 - duration_on);
count++;
}
```
第 17 行就改用 `++` 來遞增 `count` 變數的內容。
## 使用 for 迴圈簡化流程
前面的範例會需要執行切換亮/熄 100 次, 對於這種有明確重複次數的執行方式, 在 C++ 中提供有 [`for`](https://www.arduino.cc/reference/en/language/structure/control-structure/for/) 可以更清楚的表達, 我們先來看相同功能, 但不同的寫法:
```cpp=
int duration_on = 0; // 亮燈時間
void setup() {
pinMode(5, OUTPUT);
}
void loop() {
for(int count = 0; count < 100; count++) {
digitalWrite(5, LOW);
delayMicroseconds(duration_on);
digitalWrite(5, HIGH);
delayMicroseconds(100 - duration_on);
}
duration_on = (duration_on + 1) % 101;
}
```
你可以注意到幾個地方不一樣:
1. 程式開頭沒有宣告 `count` 變數。
2. 在第 8 行使用了 `for`, 它後面要跟著一對小括號, 再接著一個區塊。小括號內以**分號**區分為 3 個部分, 依序是**初始設定**、**判斷條件**、**改變動作**, 以本例來說, 就是:
```
for(int count = 0; count < 100; count++) {
------------- ----------- -------
初始設定 判斷條件 改變動作
...
}
```
整個 `for` 流程如下所示:
```flow
init=>operation: 初始設定
int count = 0;
cond=>condition: 判斷條件
count < 100
change=>operation: 改變動作
count++
block=>operation: 區塊
亮/熄 LED
exit=>operation: for 之後的敘述
init->cond
cond(no)->exit
cond(yes, right)->block(right)->change(right)->cond
```
3. `for` 會先執行**初始設定**, 以本例來說就是宣告並定義變數 `count` 為 0。
4. 執行**判斷條件**, 如果成立, 就執行 `for` **區塊**;否則就會跳到區塊之後執行。以本例來說就是檢查 `count` 是否小於 100?若是就執行 9~12 行, 否則就跳到第 14 行。
5. 每次執行 `for` 的區塊後會執行**改變動作**, 以本例來說就是把變數 `count` 的內容加 1。然後回到步驟 4.。
1. 根據上述, 8~13 行的 `for` 會讓 `count` 從 0 開始, 變化到 100 時跳離, 所以 `for` 區塊會執行 0~99 共 100 次, 實際的動作就是讓 9~12 行的亮/熄切換進行 100 次。
1. 第 14 行是在跳離 `for`, 也就是亮/熄切換 100 次後, 將亮燈時間增加 1us。第 15 行則是檢查並讓點亮時間超過 100us 時重置回 0us。
你可以重新上傳程式, 確認和上一個範例的結果完全相同。
:::info
像是 `for` 這樣用來重複執行動作的結構稱為**迴圈 (loop)**。
:::
## 使用雙重迴圈再度簡化程式
了解 `for` 的用法後, 你可能已經想到點亮時間的變化也是一個從 0 到 100 的規律變化, 是不是也可以使用 `for` 來簡化呢?當然可以, 我們先來看簡化的版本:
```cpp=
void setup() {
pinMode(5, OUTPUT);
}
void loop() {
for(int duration_on = 0; duration_on <= 100; duration_on++) {
for(int count = 0;count < 100; count++) {
digitalWrite(5, LOW);
delayMicroseconds(duration_on);
digitalWrite(5, HIGH);
delayMicroseconds(100 - duration_on);
}
}
}
```
1. 原本程式開頭變數 `duration_on` 的宣告不見了。
2. 第 6 行多了一個 `for`, 讓 `duration_on` 從 0 變化到 100。
3. 比較特別的是這個 `for` 的區塊內又有一個 `for`, 也就是用來切換亮/熄的迴圈, 實際的效果就是內層的 `for` 一結束, 外層的 `for` 就會執行**改變動作**, 再執行**判斷條件**, 若成立, 就再執行內層的 `for`。
這個程式的效果還是一樣, 只是因為改用雙層迴圈, 所以程式從原本的 15 行再縮短到 14 行了。由於使用迴圈的關係, 不但程式可以變得更簡潔, 也讓『變數在某個數值區間內遞增變化』的意涵更清楚。
## 加上反向迴圈設計呼吸燈
`for` 迴圈極具彈性, 你也可以反向控制, 像是剛剛的範例都是讓 LED 漸亮, 如果反向變化, 就可以讓 LED 漸暗, 底下就是把漸亮和漸暗組合在一起的程式:
```cpp=
void setup() {
pinMode(5, OUTPUT);
}
void loop() {
for(int duration_on = 0; duration_on <= 100; duration_on++) {
for(int count = 0;count < 100; count++) {
digitalWrite(5, LOW);
delayMicroseconds(duration_on);
digitalWrite(5, HIGH);
delayMicroseconds(100 - duration_on);
}
}
for(int duration_on = 100; duration_on >= 0; duration_on--) {
for(int count = 0;count < 100; count++) {
digitalWrite(5, LOW);
delayMicroseconds(duration_on);
digitalWrite(5, HIGH);
delayMicroseconds(100 - duration_on);
}
}
}
```
第 14~21 就和第 6~13 幾乎一樣, 唯一的不同是 `duration_on` 是從 100 開始, 判斷條件是大於等於 0, 而改變動作是讓 `duration_on` 減 1。也就是這個 `for` 是讓 LED 亮燈時間從 100us 變化到 0us, 漸漸變暗。
:::info
要特別說明的是, 在 `for` 的初始設定中所宣告的變數僅在 `for` 敘述中有效, 因此雖然上述程式在兩個 `for` 迴圈中都宣告了相同名稱的變數, 但是彼此各自獨立, 互不相干。
:::
你現在就可以上傳程式看看效果, 由於是從不亮漸變亮, 再從最亮漸漸變暗, 如此反覆, 所以會有一種生命的韻律感, 這種效果也稱為**呼吸燈 (breathing light)**。