# 資訊之芽講義 - Loop
楊宗儒@Sprout2023
----
## Slido
[slido.com #2895036](https://app.sli.do/event/p9H8QKAVKQ3opf3C4nJssM)
---
## 先熱身吧
----
印1000行"Hello Sprout"
計時一分鐘
----

[stackoverflow.blog](https://stackoverflow.blog/2021/09/28/become-a-better-coder-with-this-one-weird-click/)
----
### 為什麼不好?
1. code品質堪憂(如果要輸出 $10^6$ 行)
2. 有可能不知道要印幾次(k個"Hello Sprout")
3. 手真的很痠
----

---
## while
----
```cpp
while(condition) {
doSomething();
}
```

condition: 迴圈**繼續**的條件
----
印1000行"Hello Sprout"
以剛剛的題目為例,繼續的條件是什麼?
:::spoiler 答案
還沒印完(廢話????)
----
怎麼知道還沒印完?
----
用數的!
----
```cpp=
int counter = 0;
while(counter<1000) {
cout<<"Hello Sprout"<<endl;
counter++;
}
```
記得0~999剛好1000個
----
迴圈只能每次都做一樣的事情嗎?
----
印1000行"i:Hello Sprout",
i為行號(1~1000)
----
```cpp=
int counter = 0;
while(counter<1000) {
cout<<"Hello Sprout"<<endl;
counter++;
}
```
或許 counter 在這裡會有用...?
----
```cpp=
int counter = 0;
while(counter<1000) {
cout<<counter+1<<": Hello Sprout"<<endl;
counter++;
}
```
----
```cpp=
int counter = 0;
while(counter<1000) {
cout<<counter+1<<": Hello Sprout"<<endl;
// counter++;
}
```
要是少了一行...?
----
### 無窮迴圈
* 當一個迴圈永遠不會停止時
* 你們的第一個編譯器(通常)找不出的bug
* 用 Ctrl-C 強制結束
----
### 課內練習題
OJ #801
----
### 題解
繼續的條件:不是1
每次循環做的事:
偶數:n=n/2
奇數:n = 3*n+1
次數+1
----
```cpp
int n;
cin>>n;
int cnt = 0;
while(n!=1) {
if(n%2==1) n = 3*n+1;
else n = n/2;
cnt++;
}
cout<<cnt;
```
---
## do-while
----
```cpp
do {
doSomething();
} while(conditon);
```

----
跟while很像,差別在於
* do-while是先**做事**再**判斷**
* while是先**判斷**再**做事**
----
```cpp=
int x = 1;
do {
cout<<x<<endl;
x++;
} while(x>2);
```
```cpp=
int x = 1;
while (x>2) {
cout<<x<<endl;
x++;
}
```
他們各自輸出什麼?
---
## for
----
再看一次剛剛的code:
```cpp=
int counter = 0;
while(counter<1000) {
cout<<counter+1<<": Hello Sprout"<<endl;
counter++;
}
```
while搭配counter真的很好用
於是新的語法就出現了
----
```cpp
for(initial;condition;change) {
doSomething();
}
```

----
和剛剛的while做比對:
```cpp=
int counter = 0; // initial
while(counter < 1000) { // condition
cout<<counter + 1<<": Hello Sprout"<<endl;
counter++; //change
}
```
```cpp=
for(int counter = 0; counter < 1000; counter++) {
cout << counter + 1 <<": Hello Sprout" << endl;
}
```
----
### 什麼時候用while/for?
* while: 只知道條件,不確定會做幾次
* for: 確定會做幾次
以上講的是大原則,實際有時得依靠經驗法則
建議在練習時嘗試兩個都試看看,有時候其中一種會遠比另一種好寫
但語法上兩個幾乎是可以互通的
----
### 比較少見的語法
```cpp
for(init;condition;change) {}
```
事實上init, condition和change的部分都可以空著
* init: 不做任何初始化
* condition: 永遠繼續迴圈下去(搭配break服用)
* change: 一次迴圈做完不做任何更動
```cpp
int x = 10
for(;x!=1;) {
cout<<x<<endl;
if(x%2==0) x/=2;
else x = 3*x+1;
}
```
---
## 巢狀迴圈
----
給定一正整數 $N$,輸出大小為$N$ 的'*'三角形
```
input:
5
output:
*
**
***
****
*****
```
用一層迴圈好像有點複雜...?
----
### 一個迴圈不夠,那就用兩個吧!
```cpp
for(???) {
for(???){
cout<<"*";
}
cout<<endl;
}
```
觀察:第i行會有i個星星
----
```cpp
for(int i = 1;i<=n;i++) {
for(int j = 1;j<=i;j++){
cout<<"*";
}
cout<<endl;
}
```
----
### 課內練習
OJ #893
----
## 題解
觀察:第i行要輸出 $n-i$個空格+ $i$個i
---
## continue/break
----
給定數字k,輸出1~k中所有不是2, 3, 5 的倍數的數字
```
input: 20
output: 1 7 11 13 17 19
```
### 有什麼不一樣:
* 不知道下一個數字要加多少
* 要是能直接跳過一次迴圈就好了...
----
解法1. if
```cpp
for(int i = 1;i<=k;i++) {
if(i%2!=0) {
if(i%3!=0) {
if(i%5!=0) {
cout<<i<<" ";
}
}
}
}
```
好...醜...
* 過多層的if會讓code可讀性變差
* 想排除的數字一多後會是個災難
----
### 語法: continue

----
```cpp
for(int i = 1;i<=k;i++) {
if(i%2==0) continue;
if(i%3==0) continue;
if(i%5==0) continue;
cout<<i<<" ";
}
cout<<endl;
```
----
### 語法: break

----
```cpp
for(int i = 1;i<20;i++) {
cout<<i<<" ";
if(i%12==0)break;
}
```
```
1 2 3 4 5 6 7 8 9 10 11 12
```
----
### 巢狀迴圈+(break, continue)
```cpp=
for(int i = 0;i<3;i++) {
for(int j = 0;j<3;j++) {
if(i==1) break;
cout<<i<<" "<<j<<endl;
}
}
```
原則: break跟continue只會影響第一個包住它的迴圈
----
```
0 0
0 1
0 2
2 0
2 1
2 2
```
---
## 常見迴圈技巧
----
### 讀到沒有為止
有時候測資會要你讀一堆數字,但沒有告訴你有多少數字,這時就需要搭配while來重複讀資料(讀到EOF(End Of File))
```cpp!
int n;
while(cin>>n) {
doSomething();
}
```
----
### 0\~n-1\/1\~n
題目時常會要你對0\~n-1或1\~n做事,用for的話會長以下的樣子:
```cpp!
for(int i = 0;i<n;i++) {
// 0~n-1 (共n個數字)
}
```
```cpp!
for(int i = 1;i<=n;i++) {
// 1~n (共n個數字)
}
```
----
### 一次跳出兩層迴圈
剛剛說過,break只能跳出最近的一層迴圈,如果想要一次跳出兩層迴圈,可以用以下的方法:
```cpp
for(int i = 0;i<n;i++) {
bool flag = false;
for(int j = 0;j<i;j++) {
if(condition) {
flag = true;
break;
}
doSomething();
}
if(flag) break;
}
```
----
這些技巧請不要特別去死背,用到時慢慢想,通常結果還是會跟上面的長得差不多,用的多最後自然就會記得了。
---
## Coding Style
----
當一個程式出現巢狀迴圈和if的東西時,如果沒有將code排列整齊,會讓人無法看出每個區塊的包含關係
```cpp!
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i + j == 2) {
if (i == 0) {
cout << "yes" << endl;
} else {
cout << "no" << endl;
}
} else {
cout << "idk" << endl;
}
}
}
```
----
如果這樣排的話:
```cpp!
for (int i = 0;i<n;i++) {for (int j = 0;j<n;j++) {
if (i+j==2)
{
if (i==0) {
cout<<"yes"<<endl;
} else {
cout<<"no"<<endl;
}
} else {
cout<<"idk"<<endl;
}}}
```
一個禮拜後的你和你的同事都會想要把你給殺了。
每多一層迴圈/if,請多一個縮排(tab/4 spaces)
----
此外,關於if/for還有一些規範(~~宗教戰爭~~):
```
for<space/nospace>(int i = ...)
```
```
for()<space/nospace>{
```
```
for()
{
}
or
for(){
}
```
----
到目前為止只要挑一個自己習慣的即可,唯一的要求是整份code請保持一致,再邪門的邪教也比不過所有邪教混在一起。到未來工作時再遵守團隊的格式要求。
{"metaMigratedAt":"2023-06-17T22:37:20.098Z","metaMigratedFrom":"YAML","title":"資訊之芽講義 - Loop","breaks":true,"contributors":"[{\"id\":\"e36da324-767a-4783-bb75-763f69fce89a\",\"add\":6515,\"del\":678}]"}