# 資訊之芽講義 - Loop 楊宗儒@Sprout2023 ---- ## Slido [slido.com #2895036](https://app.sli.do/event/p9H8QKAVKQ3opf3C4nJssM) --- ## 先熱身吧 ---- 印1000行"Hello Sprout" 計時一分鐘 ---- ![](https://i.imgur.com/J8WUh6P.png) [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. 手真的很痠 ---- ![](https://i.imgur.com/u5JmZYH.jpg) --- ## while ---- ```cpp while(condition) { doSomething(); } ``` ![](https://i.imgur.com/4CrI5Uc.jpg) 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); ``` ![](https://i.imgur.com/kpkzH96.jpg) ---- 跟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(); } ``` ![](https://i.imgur.com/jGvVN3s.jpg) ---- 和剛剛的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 ![](https://i.imgur.com/len1nao.jpg) ---- ```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 ![](https://i.imgur.com/09HBIGs.jpg) ---- ```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}]"}
    821 views