這是構建之法 《現代軟件工程》課的做業題之一。 html
下面的題目, 從簡單的命令行處理和數據處理開始開始,讓同窗們逐步練習,鞏固算法,學會鬆耦合的設計,學會PSP,源代碼控制,單元測試,迴歸測試,增量改進程序,等等。前端
第一步: 像《構建之法》的人物阿超那樣,花二十分鐘寫一個能自動生成小學四則運算題目的命令行 「軟件」。git
具體要求:任何編程語言均可以,命令行程序接受一個數字輸入,而後輸出相應數目的四則運算題目和答案。例如輸入數字是 30, 那就輸出 30 道題目和答案。 運算式子必須至少有兩個運算符,運算數字是在 100 以內的正整數,答案不能是負數。 如:程序員
23 - 3 * 4 = 11github
擴展要求:看看小學3、4、五年級的四則運算要求, 逐步實現各個年級的難度。 寫博客紀錄本身實現每個擴展的思路。 在擴展的過程種,你是獨立開發每一個年級的運算題的功能,處理四年級的代碼和三年級的代碼沒有任何聯繫,仍是逐步把一些公共的代碼放在一塊兒? 或者你用面向對象的方法,運用基類 (base class) 和子類來管理各類不一樣的需求和實現的細節? 算法
第二步, 分別知足下面的各類需求。編程
從這一步開始,咱們要求學生填寫我的軟件流程 PSP 的耗時估計和實際時間的表格,要求把代碼簽入到源代碼管理服務器上。 數組
下面這些需求均可以用命令行參數的形式來指定:服務器
a) 一次能夠出一千道道題目,而且沒有重複的,把題目寫入一個文件中。咱們你們都知道,(1+2) 和 (2+1) 是重複的題目。 高級要求: 怎麼嚴格定義題目重複呢,請看詳細題目要求數據結構
和同窗們比較一下各自程序的功能、性能、實現方法的異同等等。
b) 當你有多於一個運算符的時候,如何對一個表達式求值?逐步擴展功能和能夠支持的表達式類型,最後但願能支持下面類型的題目 (最多 10 個運算符,括號的數量不限制):
25 - 3 * 4 - 2 / 2 + 89 = ?
1/2 + 1/3 - 1/4 = ?
(6 - 4 ) * (3 + 28) =?
提示:不少學生在開始的時候用簡單的條件判斷來處理運算,若是隻有一個運算符,那仍是比較簡明的,運算符多了以後,怎麼辦呢? 一些學生就用不少條件判斷來處理運算的優先級,這是某學生代碼片斷:
public void jisuan(double a, string operation1,double b, string operation2,double c, string rightanswer)
{
bool aa = false;
if (operation1 == "+" || operation1 == "-")
{
if (operation2 == "*" || operation2 == "/")
{
aa = true;
}
else
{
aa = false;
}
}
else
{
aa = false;
}
if (aa == true)
...
那若是有 3 個運算符呢?怎麼辦,繼續增長不一樣的 if/else 來解決?4個運算符呢?... 顯然這不是一個好的算法, 一個好的算法,即便問題變得更復雜,算法自己應該依然是簡明的, 而且程序自己的代碼量並不會變得異常複雜。 咱們看看有什麼好的數據結構能高效地表示四則運算。
請參考調度場算法:
http://hczhcz.github.io/2014/02/27/shunting-yard-algorithm.html
https://en.wikipedia.org/wiki/Shunting-yard_algorithm (中文版)
c) 除了整數之外,還要支持真分數的四則運算。 (例如: 1/6 + 1/8 = 7/24 )
d) 讓程序能接受用戶輸入答案,並斷定對錯。 最後給出總共 對/錯 的數量。
e) 到目前爲止,這個程序的界面都是中文的, 隨着這個應用大受歡迎,別的國家的用戶也要用,那麼怎麼能高效地讓這個 App 支持不一樣文字界面互換呢?你是在程序裏面不斷插入 if ... else ... 來處理中英文,仍是有高效率,能夠擴展的辦法?這個程序最終會擴展爲支持10種語言,並且每一個語言的用戶須要符合他們文化的圖標。請問你仍是用 if/else 來解決麼?
例如:你的程序已經支持了兩種語言,而且中文界面有一個符合中國用戶指望的圖標,英文界面有一個符合美國用戶指望的圖標, 如今你要支持第三個語言 - 日文,而且須要一個符合日本用戶的圖標(假設是富士山的圖像)。 而後還有另外 7 個語言 (德國、法國、意大利、...)和不一樣的圖像要支持。那麼,這能夠用if/else 來解決, 仍是有更高效的辦法?
當顯示文字直接放在代碼中,例如像下面這樣:
if (isEnglish) UIControl.setText("Welcome to my calculator app"); else if (isChinese) UIControl.setText("歡迎使用個人四則運算程序"); else if ...
這種用戶顯示的文字和代碼邏輯放在一塊兒的設計,咱們稱之爲緊耦合。 程序員精通代碼邏輯,但未必精通各國語言,也不太會設計各類圖標,那咱們會讓懂法語和德語的團隊成員(或者臨時請來的專家)直接在代碼上面改麼?彷佛他們會以爲代碼編輯器的界面好難掌握哦... 改錯了,程序編譯不過去怎麼辦?咱們能夠想象,若是這些和邏輯無關的資源可以放在一個單獨的地方, 這樣負責文字和圖像的人員(他們可能不太懂編程)能去修改,而後在主程序裏,咱們把各類語言的處理抽象爲下面的操做:
UIControl.setText(GetStringByLanguage(language_Id, string_id); //根據當前顯示的語言,拿到適當的文字,而後顯示。
這樣會不會更好?邏輯代碼和現實的資源各自分開, 經過良好定義的渠道(language_id, string_id) 結合起來了, 這是一種鬆耦合。 請看同窗的嘗試1, 嘗試2.
程序能夠用不少辦法來提升效率保持簡明:抽象 (把複雜的操做抽象爲一個名字/類/函數,把複雜的處理隱藏在內),循環(高效地把相似的事情作 N 遍),遞歸(在處理問題的時候,發現這個子問題也是相似的,那我就調用本身來處理),組合(把各個操做組合起來完成複雜任務)... 等
第三步,增長一個運算符,程序應該有怎樣的改變?不得不扔掉所有重寫麼,仍是能夠只改部分模塊?
一些高年級的老師看到這個程序,但願讓他們的同窗也來用,可是有一個新需求, 要支持乘方 (power) 運算,咱們都知道,乘方運算的優先級高於乘除法。如何表示乘方, 有兩種表示方法:
1. 4 ^ 2 = 16, 4 的二次方等於 16。 這裏, ^ 表示乘方
2. 4 ** 2 = 16, 4 的二次方等於16。這裏, ** 表示乘方 (** 之間不能有空格,不然是錯誤的算式)
因爲歷史的緣由, 不一樣的學校用了不一樣的表示方法, 老師但願這兩種表示方法都要支持,能夠經過設置來選擇。
第4步,每一個同窗選一個方向,把程序擴展一下:
a) 把程序變成一個 Windows/Mac/Linux 電腦圖形界面的程序 (取決於你目前使用的電腦),同時增長 「倒計時」 功能, 每一個題目必須在 20 秒鐘完成,若是完不成,則得0 分並進入下一題。增長「歷史紀錄」 功能, 把用戶作題的成績記錄下來並能夠展示歷史記錄。
b) 把程序變成一個智能手機程序 (你正在用什麼手機, 就寫那個手機的程序), 增長倒計時,和歷史紀錄功能(見上)。
c) 把程序變成一個網頁程序, 用戶經過設定參數,就能夠獲得各類題目。
d) 選一個你歷來沒有學過的編程語言,試一試實現基本功能。
估計作好這個軟件須要的時間,而且寫出大概的設計步驟和實現算法。
e)把這個程序的思路變成一個能夠一步一步演示的步驟, 能夠是命令行的輸出, 也能夠是圖形界面:
輸入:一個正常的四則運算句子
輸出:程序用動畫表示分詞的過程,後序轉換的過程,處理不一樣運算符優先級的過程, 逐步算出得出結果的過程。
例如:輸入是 (6^2 - 4 ) * (3 + 28)
輸出是
(36 - 4 ) * (3 + 28)
32 * (3 + 28)
32 * 31
992
若是能作到這一步, 這個程序所牽涉到的 「知識點」 就能說 「精通」 了。
能夠開始作相關的第二個做業。
第三步,程序理解和擴展
咱們說了程序要能輸出不重複的題目,你們有不一樣的實現方法,那麼可否比較一下各自的異同? 例如這個實現方法:
https://gist.github.com/vczh/2c058aed996effc0a519ed3d265a3eb5
1) 可否讀一下這個程序,註釋一下它的核心設計是什麼,寫一篇博客分析一下?
2) 若是咱們要讓這個程序增長一個運算 -- 乘方運算。 你要在現有程序中作什麼樣的修改,才能讓這個程序比較優雅地實現這個新的需求。
=========================
其餘題目:
- 四則運算練習
- 計算程序文件的行數 WC
- 電梯調度