設計可完成 分數加減乘除 的程式

 

  實 習 內 容







繼我們練習寫過 檔案輸出入, 三位數字轉換中文輸出, 簡易計算機, 尋找完全數, 計算最大公因數 的程式之後, 這個實習中我們練習

1. 綜合運用 條件敘述, 迴圈, 函數 來模組化地設計程式

2. 撰寫一個可以完成 "分數加減乘除運算" 的程式

1

這個實習中, 我們要設計一個可以完成 "分數加減乘除運算" 的程式

操作者可以輸入兩個普通分數 (common fraction, 2/3, 7/2...), 以及運算符號, 程式會完成指定的運算並且用最簡分數列印運算的結果

範例執行程式

執行過程如下:

Enter a common fraction as two integers separated by a slash> 2/3
Enter an arithmetic operator (+,-,*, or /) > +
Enter a common fraction as two integers separated by a slash> 5/9

2/3 + 5/9 = 11/9
Do another problem? (y/n)> y
Enter a common fraction as two integers separated by a slash> 2/3
Enter an arithmetic operator (+,-,*, or /) > -
Enter a common fraction as two integers separated by a slash> 1/6

2/3 - 1/6 = 1/2
Do another problem? (y/n)> y
Enter a common fraction as two integers separated by a slash> 2/3
Enter an arithmetic operator (+,-,*, or /) > *
Enter a common fraction as two integers separated by a slash> 9/8

2/3 * 9/8 = 3/4
Do another problem? (y/n)> n

2

main() 函數:

這個程式和作業一互動的功能有點接近, 所以 main() 函數中最主要的架構會是如下的迴圈, 包括 1. 讀取資料及運算命令, 2. 進行指定的運算並且化簡, 3. 列印執行結果:

do
{
    /* 讀取第一個分數 */
    /* 讀取運算子 op */
    /* 讀取第二個分數 */

    switch (op) /* 判斷運算的種類  */
    {
    case '+':
        /* 執行加法運算 */
        break;
    case 
...... } /* 化簡分數 */ /* 列印結果 */ printf("\nDo another problem? (y/n)> "); scanf(" %c", &again); } while (again == 'y' || again == 'Y');

接下來可以針對各個部份撰寫函數來完成指定的功能

因為這樣子分工以後 (這也就是所謂由上而下 (top-down) 的程式設計方法), 每一個函數所需要處理的工作都變得很簡單, 當然也要求你一定要讓每一個函數所處理的資料 (輸入哪些資料, 輸出哪些資料) 很明顯地運用函數的參數表示出來 (請不要使用全域變數)

3

讀取分數函數

每一個分數包括三部分: 分子, /, 分母, 例如:

3 / 4
 5/   6
  7    /8
9 / 10   運算元

都是合法的輸入

這個函數的定義應該類似 void scan_fraction(int *numerator, int *denominator);

函數裡面我們可以用 iret = scanf("%d %c%d", numerator, &slash, denominator); 來讀取使用者由鍵盤輸入的資料, 第一個 %d 會自動跳過所有的空格, 讀入十進位的整數分子, %c 不會跳過空格, 所以前面需要加上一個空格來要求 scanf 跳過所有空格, 接下來的 %d 可以自動跳過空格讀入十進位的整數分母

scanf() 函式如果讀到了三筆資料的話, 回傳的數值 iret 就是資料的筆數 3, 同時 slash == '/', 如果兩者有任何不正確的話就需要印出訊息提示使用者重新輸入

另外, 分母之後一直到換列字元的文字如果是對程式無意義的注釋, 需要使用一個迴圈跳過, 例如:

char discard;
...
do { scanf("%c", &discard); } while (discard != '\n');
函數寫完以後請儘快編譯, 測試執行的結果, 不要等到整個程式寫完以後才編譯, 才測試。以這個函數來說, 就可以在函數執行完回到 main 的時候印出 分子和分母來看一下, 就知道是否正確。

4

讀取運算子函數

這個函數的定義應該類似 char get_operator(void);

函數裡面包括一個迴圈, 運用 scanf 函數讀取字元, 檢查是不是可以接受的 +, -, *, /, 如果不是的話, 印出訊息提示使用者重新輸入

5

執行加法運算的函數

這個函數傳進去兩個分數, 回傳兩個分數相加的結果 (因為在 main() 中我們在執行完運算後才統一執行分數化簡的動作, 所以這個函數加起來的分數不需要化簡), 函數的定義如下:

void add_fractions(int num1, int  denom1,
                   int num2, int  denom2,
                   int *num_ansp, int *denom_ansp); 

num1, denom1 代表傳入的第一個分數, num2, denom2 代表傳入的第二個分數, num_ansp, denom_ansp 則是為了把結果傳出函數的兩個記憶體位址

分數的加法需要通分, 計算兩個分母的乘積作為新的分母, 例如

*denom_ansp = denom1 * denom2;

新的分子也可以很快算出

統一把正負號放在新的分子裡面, 分母讓它維持是正數

為了盡快可以看到程式執行的結果, 測試每一部份的程式的正確性, 下一步驟裡可以先寫列印結果的函數

6

列印結果的函數:

在 main 函數裡迴圈的最後, 需要寫一個列印結果的函數, 傳入兩個分數和運算子, 再傳入化簡過的分數結果, 運用 printf 函數輸出如下的運算式:

2/3 * 9/8 = 3/4

7

執行減法運算的函數

其實這個函數應該可以不用寫, 直接呼叫前面執行加法運算的函數 add_fractions, 將第二個分數變號就可以完成了

8

執行乘法運算的函數

這個函數傳進去兩個分數, 回傳兩個分數相乘結果 (因為在 main() 中我們在執行完運算後才統一執行分數化簡的動作, 所以這個函數加起來的分數不需要化簡), 函數的定義如下:

void mult_fractions(int num1, int  denom1,
                    int num2, int  denom2,
                    int *num_ansp, int *denom_ansp); 
乘法的動作比加減法更簡單, 基本上分子分子相乘, 分母分母相乘就快要好了, 只需要檢查一下是否為最簡分數

9

執行除法運算的函數

其實這個函數應該可以不用寫, 直接呼叫前面執行乘法運算的函數 mult_fractions, 將第二個分數分子分母互調就完成了

10

分數化簡的函數

這個函數傳進去一個分數, 回傳這個分數化簡後的結果, 函數的定義如下:

void reduce_fraction(int *nump,int *denomp); 
函數的內部首先用我們前面實習所完成的 find_gcd() 函數來找到分子和分母的最大公因數, 然後化簡過的分子就是原來的分子除以最大公因數, 化簡過的分母就是原來的分母除以最大公因數

11

合併測試: 編譯並且測試下列

  • 3 / 6 + 2 / 3
  • 1 / 8 - 3 / 8
  • 2 / 7 * 1 / 4
  • 8 / 3 / 4 / 2
相信你可以慢慢感覺到自己可以寫的程式的功能越來越強大了, 接下來我們會學到怎樣更靈活地運用記憶體來記錄演算法執行的狀態, 如此就能夠看到比較有效率, 做的事情比較有意義的演算法了

12

線上繳交: 完成上述測試以後, 請將提示使用者的文字註解掉, (例如 Enter a common fraction as two integers separated by a slash>) 然後在 e-Tutor 線上繳交

程式設計課程 首頁

製作日期: 10/14/2012 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程學系 Lagoon