--- tags: 視窗程式設計 --- # 計算機程式:實現基本四則運算 我們已經初步完成基本計算機的介面,然後也能讓它實現按下數字鍵,出現數字的效果。 接下來我們讓計算機可以選擇運算符號,再輸入第二個數字,最後按下等於按鍵,完成計算。 ## 複習一下 :::success 你可以拿出你手機裡面的計算機App,一邊操作一邊想想,你是怎麼使用計算機的? ::: 我們再回想一下,計算機是怎麼操作的?因為這會決定我們要怎麼寫程式? 之前的練習,應該已經完成第一個項目,現在我們要完成2-4的項目。 :::info 1. ~~輸入第一個數字:輸入一個數字,從最左邊的數字輸入到最後一個數字~~ **(這個已經完成了)** 2. 選擇四則運算符號:從加、減、乘、除中挑選一個 3. 輸入第二個數字::輸入一個數字,從最左邊的數字輸入到最後一個數字 4. 按等於按鍵:得到計算結果 ::: ``` mermaid graph TD; 按下數字按鍵輸入第一個數字-->選擇要運算的四則運算符號; 選擇要運算的四則運算符號-->按下數字按鍵輸入第二個數字; 按下數字按鍵輸入第二個數字-->按下等於按鍵; 按下等於按鍵-->顯示計算結果; ``` ## 使用全域變數,紀錄輸入的第一個數字、第二個數字、選擇的四則運算符號 我們先宣告全域變數,**==順便考考你,全域變數要放在什麼地方?==** ```csharp= // 全域變數 float firstNumber, secondNumber; // firstNumber 儲存第一個數字,secondNumber 儲存第二個數字 int operators = -1; // 記錄選擇哪一種運算符號?0:加、1:減、2:乘、3:除、-1:重新設定 ``` 還記得所謂的「**全域變數**」嗎?這邊我們利用三個全域變數,來記錄「**第一個數字 firstNumber**」、「**第二個數字 secondNumber**」、「**選擇的四則運算符號 operators**」,最後三個全域變數將會組合在一起,計算結果。 :::info 如果你忘了什麼是全域變數,請點選 [**這裡**](https://hackmd.io/@chohsunlu/ByHy31u2t/%2FC9WuZRdnTG6OlnT8U3G5Yw#%E5%85%A8%E5%9F%9F%E8%AE%8A%E6%95%B8%E7%9A%84%E8%A7%80%E5%BF%B5)。 ::: 設計這三個全域變數,是為了依照前述的程式邏輯。 **我們將第一個輸入的數字,先存到 firstNumber 變數中,然後再用 operators 記錄按了哪一個四則運算符號?再依樣畫葫蘆,輸入第二個數字,存到 secondNumber 中。** 為什麼要這麼做?我們先完成四則運算按鍵的事件綁定,再來解釋為什麼要這樣設計? <!-- 最後,按下「等於」按鍵,就依照選擇的四則運算符號,把第一個與第二個數字進行計算,最後顯示在輸入文字框中,呈現計算結果。 --> ## 設定四則運算按鍵的事件綁定 剛剛我們已經說明了,我們將第一個輸入的數字,先存到 firstNumber 變數中,然後再用 operators 記錄按了哪一個四則運算符號?再依樣畫葫蘆,輸入第二個數字,存到 secondNumber 中。 那我們要在什麼時候「記錄」第一個數字呢? :::info 答案:在「**按下四則運算按鍵**」的時候。 ::: 你可以回想一下你怎麼使用計算機?當你確定第一個數字的內容,接下來按的按鍵,就是「四則運算按鍵」,可能是加減乘除的其中一個,所以在這個階段紀錄第一個數字,應該是一個合適的時機。 此外大多數的計算機,按下「四則運算按鍵」後,也同時將輸入文字框重新設定為 0 ,並且也同時記錄選了哪一種四則運算的符號。 因此請同學以下參考的程式碼,請試著做好這四個按鍵的事件綁定,再將程式加入。你也可以仔細看一下,每一行程式在做甚麼?應該都符合以上的設計。 然後可以試著測試程式看看,應該輸入第一個數字之後,選擇任一個四則運算符號,就會同時儲存第一個數字,也儲存了選擇的運算符號,同時,輸入文字框也會重設為 0。 ```csharp= // 按下選擇「加」按鍵 private void btnAdd_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第一個數字的全域變數 txtNumber.Text = "0"; //重新將輸入文字框重新設定為0 operators = 0; //選擇「加」號 } // 按下選擇「減」按鍵 private void btnMinus_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 1; //選擇「減」號 } // 按下選擇「乘」按鍵 private void btnPlus_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 2; //選擇「乘」號 } // 按下選擇「除」按鍵 private void btnDivide_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 3; //選擇「除」號 } ``` ## 回到全域變數 這時我們再回到全域變數的設計,為什麼要設計那三個全域變數,仔細看看以上四則運算的程式碼,你應該會發現,四個事件綁定的程式碼區域,都會用到「**第一個數字 firstNumber**」與「**選擇的四則運算符號 operators**」,因為這四個區域都要控制這兩個變數的內容,所以你必須要將這兩個變數設定為全域變數。 ## 輸入第二個數字 這時候你也會發現,再直接輸入數字,一樣也可以輸入,所以這部分也OK了,不需要寫額外的程式碼。 然後你可以想想看,那麼**什麼時候可以儲存二個數字呢?** :::info 答案:在「**按下等於按鍵**」的時候。 ::: ## 設定等於按鍵的事件綁定 由於按下等於按鍵,相當於要完成計算過程,因此,我們在等於按鍵裡面,來儲存第二個輸入的數字,並且完成最後計算。 請將等於按鍵的事件綁定做好,再將以下程式加入。 ```csharp= private void btnEqual_Click(object sender, EventArgs e) { float finalResults = 0f; //宣告最後計算結果變數 secondNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第二個數字的全域變數 //依照四則運算符號的選擇,進行加減乘除 switch (operators) { case 0: finalResults = firstNumber + secondNumber; break; case 1: finalResults = firstNumber - secondNumber; break; case 2: finalResults = firstNumber * secondNumber; break; case 3: finalResults = firstNumber / secondNumber; break; } txtNumber.Text = string.Format("{0:0.##########}", finalResults); //在輸入文字框中,顯示最後計算結果,並且轉換成格式化的字串內容 //重置所有全域變數 firstNumber = 0f; secondNumber = 0f; operators = -1; } ``` 這裡稍微說明一下程式的內容。 :::success * 第3行:宣告一個用來儲存「**最後計算結果 finalResults**」的變數,用來儲存最終的計算結果。你應該會發現,這個變數是所謂的「區域變數」,因為這個變數只有在這個等於按鍵事件綁定才有作用,而且我們也只需要在這裡有作用就好,不必做成全域變數。 * 第4行:將第二個數字存到「**第二個數字 secondNumber**」。 * 第7-21行:使用一個 **switch case 的條件判斷式**,依照當初選擇的四則運算符號,將第一個數字和第二個數字進行加減乘除的計算,最後存到「**最後計算結果 finalResults**」。 * 第23行:把「**最後計算結果 finalResults**」變數的資料,將它轉型成為格式化的字串,顯示在輸入文字框中,完成計算! * 第26-28行:最後一小段重置所有全域變數,通通重新設定為初始值,這樣可以讓我們重新再做計算。 ::: 可能有的同學不熟悉甚麼是 switch case 條件判斷式?以下我們繼續說明。 ## switch case 條件判斷 switch case 可用來「連續」比較數值或字元,讓你能夠一次將多個條件一同進行判斷,語法架構如下: ```csharp= switch(變數名稱或運算式) { case 符合數字或字元: 陳述句一; break; case 符合數字或字元: 陳述句二; break; default: 陳述三; break; } ``` 首先看看 switch 的括號,當中置放要判斷的變數或運算式,會與 case 設定的數字或字元比對,如果符合就執行 case 以下的陳述句,直到遇到 break 後離開 switch 區塊,若沒有符合的數值或字元,會執行 default 後的陳述句,如果你沒有預設項目,default 可以省略。 其實 switch 你可以看成一個「**==連續的 if 判斷式==**」,我們可以用以上的範例來說明。 原來是: ```csharp= switch (operators) { case 0: finalResults = firstNumber + secondNumber; break; case 1: finalResults = firstNumber - secondNumber; break; case 2: finalResults = firstNumber * secondNumber; break; case 3: finalResults = firstNumber / secondNumber; break; } ``` 如果使用 if 判斷式: ```csharp= if (operators == 0) finalResults = firstNumber + secondNumber; if (operators == 1) finalResults = firstNumber - secondNumber; if (operators == 2) finalResults = firstNumber * secondNumber; if (operators == 3) finalResults = firstNumber / secondNumber; ``` 上面和下面基本上都是一樣的結果(不信的話你可以丟進去試試看),那麼哪一種比較好呢?這要看情形,如果你的多條件判斷很單純,例如我們的案例,只要看是哪一種數值的情境,switch case 看來就挺好用的。但如果你的多條件判斷比較複雜,又需要在每一種判斷結果進行其他的判斷,可能還是一般的 if else 比較合適。 隨著程式的複雜度提高,有些程式設計師反倒認為 switch case 過時了,有的程式語言甚至沒有 switch case 語法,例如 Python 就沒有,你要做類似的效果還是只能寫 if else。 ## 小數點的輸入 還有一個東西沒有完成,就是小數點按鍵的處理,你可以回去使用計算機試試看,小數點按鍵的特點是「如果輸入文字框已經有小數點了,就不能再輸入小數點」。所以必須要先判斷有沒有小數點,才能讓程式在數字後面加上小數點。 ```csharp= private void btnDot_Click(object sender, EventArgs e) { // 確認輸入文字框中完全沒有小數點 if (txtNumber.Text.IndexOf(".") == -1) txtNumber.Text = txtNumber.Text + "."; } ``` 我們怎麼實現檢查有沒有小數點?在 C# 中,我們可以使用「**==IndexOf()==**」方法,他可以用來顯示要找尋的字串首次出現的從零開始的位置。如果找不到這個字串,則該方法回傳「-1」。 以下是IndexOf() 方法的語法: ```csharp= 要搜索的原始字串.IndexOf(要尋找的字串, 開始搜索的位置, 搜索結束的位置) ``` 其中「開始搜索的位置」與「搜索結束的位置」可以省略,就會搜尋整個字串內容。 因此以上的程式片段意思就是:我們要找尋的是小數點字元「.」,並且判斷有沒有等於「-1」。 如果是「-1」,代表就是沒有小數點,這時候才在輸入的數字後面加上小數點。 如果不是「-1」,也就是有小數點,就不增加小數點,避免連續出現小數點。 ## 清除按鍵的事件綁定 最後,我們把清除按鍵完成,清除按鍵相當於重新設定所有全域變數,然後將輸入文字框重置顯示為「0」,請參考以下程式碼。 ```csharp= private void btnClear_Click(object sender, EventArgs e) { txtNumber.Text = "0"; firstNumber = 0f; secondNumber = 0f; operators = -1; } ``` ## 結語 到目前為止,我們已經初步完成這個小型計算機的所有功能,以下是完整的參考原始碼。 當然,這不是最後答案,我們還是要針對它做點小修改,讓整個程式更簡潔一點。 ```csharp= using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Calculator { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // 全域變數 float firstNumber, secondNumber; // firstNumber 儲存第一個數字,secondNumber 儲存第二個數字 int operators = -1; // 記錄選擇哪一種運算符號?0:加、1:減、2:乘、3:除、-1:重新設定 private void btnOne_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") { txtNumber.Text = ""; } txtNumber.Text = txtNumber.Text + "1"; // txtNumber.Text += "1"; // 上面和下面的寫法意思是一樣的 } private void btnTwo_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") // 如果你的判斷式簡單到只有一行程式,可以把 { } 大刮號省略掉 txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "2"; } private void btnThree_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "3"; } private void btnFour_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "4"; } private void btnFive_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "5"; } private void btnSix_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "6"; } private void btnSeven_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "7"; } private void btnEight_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "8"; } private void btnNine_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = ""; txtNumber.Text = txtNumber.Text + "9"; } private void btnZero_Click(object sender, EventArgs e) { if (txtNumber.Text == "0") txtNumber.Text = txtNumber.Text + "0"; } // 按下選擇「加」按鍵 private void btnAdd_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第一個數字的全域變數 txtNumber.Text = "0"; //重新將輸入文字框重新設定為0 operators = 0; //選擇「加」號 } // 按下選擇「減」按鍵 private void btnMinus_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 1; //選擇「減」號 } // 按下選擇「乘」按鍵 private void btnPlus_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 2; //選擇「乘」號 } // 按下選擇「除」按鍵 private void btnDivide_Click(object sender, EventArgs e) { firstNumber = Convert.ToSingle(txtNumber.Text); txtNumber.Text = "0"; operators = 3; //選擇「除」號 } private void btnEqual_Click(object sender, EventArgs e) { float finalResults = 0f; //宣告最後計算結果變數 secondNumber = Convert.ToSingle(txtNumber.Text); //將輸入文字框轉換成浮點數,存入第二個數字的全域變數 //依照四則運算符號的選擇,進行加減乘除 switch (operators) { case 0: finalResults = firstNumber + secondNumber; break; case 1: finalResults = firstNumber - secondNumber; break; case 2: finalResults = firstNumber * secondNumber; break; case 3: finalResults = firstNumber / secondNumber; break; } txtNumber.Text = string.Format("{0:0.##########}", finalResults); //在輸入文字框中,顯示最後計算結果,並且轉換成格式化的字串內容 //重置所有全域變數 firstNumber = 0f; secondNumber = 0f; operators = -1; } private void btnDot_Click(object sender, EventArgs e) { // 確認輸入文字框中完全沒有小數點 if (txtNumber.Text.IndexOf(".") == -1) txtNumber.Text = txtNumber.Text + "."; } private void btnClear_Click(object sender, EventArgs e) { txtNumber.Text = "0"; firstNumber = 0f; secondNumber = 0f; operators = -1; } } } ```