Git地址 | https://github.com/F0urty-Tw0 |
Git用戶名 | F0urty-Tw0 |
學號后五位 | 61109 |
博客地址 | https://www.cnblogs.com/FourtyTwo/ |
作業鏈接 | https://edu.cnblogs.com/campus/xnsy/Autumn2019SoftwareEngineeringFoundation/homework/7590 |
Part 1. 配置環境及工具准備
由於想進一步鞏固一下Python,此次作業打算用它來寫,原先要求的Visual Studio 2017
的環境配置就由PyCharm
來代替
①PyCharm安裝
PyCharm是由JetBrains打造的一款Python IDE,進入官網https://www.jetbrains.com/pycharm/,選擇免費的community版本下載即可,如圖所示:
安裝過程較為簡單,未遇到問題,安裝完成效果如下:
②Git安裝
進入Windows版git下載地址:https://git-scm.com/download/win,一般來說打開即自動開始下載任務,若長時間未開始,可手動點擊:
接下來點擊安裝程序一步步來即可,安裝成功后應可以在桌面或文件夾空白處右鍵下拉菜單中出現Git GUI here以及Git Bash here,如下圖所示:
③項目克隆
再安裝好Git之后,我們就可以再Github上克隆指定的項目,首先進入本次作業指定的倉庫地址:https://github.com/Cherish599/AchaoCalculator,點擊右上角的Fork
之后,即可拷貝到自己的同名倉庫中。
接下來在我的電腦中任意路徑下點擊鼠標右鍵,選擇Git Bash here,輸入命令行git clone https://github.com/Cherish599/AchaoCalculator.git
,(git clone后的鏈接即為倉庫地址)如下圖所示:
之后需要在剛剛的路徑下新建一個與Github賬戶名相同的文件夾,如下圖所示:
由於之前通過Github搭建過個人博客,有了安裝及使用Git的經驗,所以本次作業在Git安裝及項目克隆方面沒有出現什么問題
Part 2.代碼實現
①題目背景:
阿超家里的孩子上小學一年級了,這個暑假老師給家長們布置了一個作業:家長每天要給孩子出一些合理的,但要有些難度的四則運算題目,並且家長要對孩子的作業打分記錄。
作為程序員的阿超心想,既然每天都需要出題,那何不做一個可以自動生成小學四則運算題目與解決題目的命令行 “軟件”呢。他把老師的話翻譯一下,就形成了這個軟件的需求:
- 程序接收一個命令行參數 n,然后隨機產生 n 道加減乘除(分別使用符號+-*/來表示)練習題,每個數字在 0 和 100 之間,運算符在 2 個 到 3 個之間。
- 由於阿超的孩子才上一年級,並不知道分數。所以軟件所出的練習題在運算過程中不得出現非整數,比如不能出現 3÷5+2=2.6 這樣的算式。
- 練習題生成好后,將生成的 n 道練習題及其對應的正確答案輸出到一個文件 subject.txt 中。
- 當程序接收的參數為4時,以下為一個輸出文件示例。
- 13+17-1=29
- 11*15-5=160
- 3+10+4-16=1
- 15÷5+3-2=4
②代碼設計思路
根據題目背景,得知設計需求為一個可以自訂題目數量的四則運算生成器,且結果只能為整數,還要將所有的算式和正確答案輸出到一個文件中。由此,這個題目的關鍵由三部分構成:表達式的隨機生成、表達式的計算、表達式及答案打印輸出
(程序由包含表達式生成、打印輸出及main()函數的caculator.py文件與包含表達式計算函數的caculate_func.py文件構成)
-
表達式的隨機生成思路及代碼實現
題目中要求表達式有兩到三個運算符,所以在表達式生成之前應先設定在2和3之間的隨機數,並通過條件分支語句判斷該隨機數為2還是3以進行后續的隨機表達式生成。
具體代碼:
r = random.randint(2, 3)
再確定運算符數量后,聲明一個列表“op”,其元素即為["+", "-", "*", "/"]四個運算符(此處乘號與除號未直接采用“×”與“÷”是為了方便后續的計算操作),接着將0到100的隨機數存入另一列表以便后續插入。在之后的表達式生成時,先取數值列表中的一個隨機值,后通過for in語句將數值循環插入,運算符除插入位置變更外同理。
具體代碼:for i in range(0, 3): tmp = random.choice(num) formula_list.append(tmp) for i in [1, 3]: tmp = random.choice(op) formula_list.insert(i, tmp)
該功能完整代碼如下:
def new_form(): # 生成新的表達式 op = ["+", "-", "*", "/"] r = random.randint(2, 3) if r == 2: # 通過判斷隨機生成的r來確定表達式中運算符數量 num = random_num(10) formula_list = [] for i in range(0, 3): tmp = random.choice(num) formula_list.append(tmp) for i in [1, 3]: tmp = random.choice(op) formula_list.insert(i, tmp) elif r == 3: num1 = random_num(10) formula_list = [] for i in range(0, 4): tmp = random.choice(num1) formula_list.append(tmp) for i in [1, 3, 5]: tmp = random.choice(op) formula_list.insert(i, tmp) return formula_list
通過網上查閱,發現python中可以使用flatten()函數或
list(itertools.chain.from_iterable(zip(l,m)))
語句來直接實現列表的交叉合並,這樣一來表達式的生成一兩條語句便可實現,但我在實際使用過程中出現報錯語句:TypeError: zip argument #1 must support iteration
並且一直無法解決(T_T),只能采用上述較為原始的方法。。。 -
表達式的計算思路及代碼實現
未避免代碼看起來冗長,將實現這一功能的函數寫入了另一個文件中,及“calculate_func.py”,並通過
python from calculate_func import gen_list, calculate
語句引入至原文件由於運算符有加減乘除四個,要將表達式的答案計算出來就要考慮到運算符的優先級,原本想着要用棧實現,但在網上搜索的過程中發現也可以利用python的列表加遞歸實現簡單加減乘除的計算。
具體原理為通過new_form()生成的表達式以列表形式存儲,之后從左向右計算所有乘除號兩邊的數值,然后刪除運算符及其左右兩個數值,再把運算結果保存在第一個數值原先存在的位置,然后第二次循環計算剩下的加減法運行,直至只剩下一個數,即表達式的運算結果,由於不能出現分數,可以讓強轉int后的結果與原結果比較,若相同則正常返回結果
具體代碼如下:def calculate(expression, isFirst=True): i = 0 while i < len(expression) - 3: a = expression[i] # 獲取操作數1 b = expression[i + 2] # 獲取操作數2 op1 = expression[i + 1] # 獲取運算符 result = None if op1 == "*": result = a * b elif op1 == "/": result = a / b elif not isFirst and op1 == "+": result = a + b elif not isFirst and op1 == "-": result = a - b if result is not None: expression[i] = result # 將第一個操作數替換問運算結果 del (expression[i + 1: i + 3]) # 刪除運算符號和后一個操作數 else: i += 2 # 下標偏移 if isFirst: return calculate(expression, False) elif int(expression[0]) == expression[0]: return expression[0] # 返回結果
-
表達式打印輸出思路及代碼實現
由於new_form()函數return了的表達式以列表形式存儲,並且乘號與除號還只是用"*"與"/"表示,所以首先要遍歷生成好的表達式找到乘號與除號,將其替換為“×”與“÷”。具體可以利用python中的字典來實現。
首先再定義一個字典op_dictm,即
op_dict = {"+": "+", "-": "-", "*": "×", "/": "*"}
,之后通過for in語句遍歷表達式列表,若其中存在"*"與"/",則直接替換字典中對應的value,
具體代碼:for i in range(length): if form_list[i] in op: form_list[i] = op_dict[form_list[i]]
之后在通過append直接將"="尾插進替換后的表達式。最后,通過
"".join(str(i) for i in form_list)
直接將列表中的元素拼接為字符串再輸出。在此之前,若表達式運算結果為整數則調用save_to_file()函數將該表達式存入文件,若不為整數,則再次調用new_form()函數與form_display()函數來重新生成並輸出表達式,直至該表達式答案為整數。
具體代碼如下:def form_display(form_list): # 將生成的表達式打印 expression = "".join(str(i) for i in form_list) expression1 = gen_list(expression) result = calculate(expression1, isFirst=True) if result: # 若通過calculate()函數計算表達式結果為整數,則進行后續的打印輸出 op_dict = {"+": "+", "-": "-", "*": "×", "/": "÷"} op = ["+", "-", "*", "/"] length = len(form_list) for i in range(length): if form_list[i] in op: form_list[i] = op_dict[form_list[i]] # 通過op_dict字典將原有表達式中的乘除運算符替換為"X"與"÷" form_list.append("=") form_list = "".join(str(i) for i in form_list) # 將form_list列表中的元素拼接為字符串輸出 print (form_list) save_to_file('subject.txt', form_list+str(result)) else: # 若表達式計算結果不為整數則不打印任何數據 form_list = new_form() form_display(form_list)
-
運行結果
Part 3.測試
①單元測試
首先選中想要進行單元測試的函數,選中后ctrl+shift+T,如圖:
選擇Create New Test.....,之后再彈出的選項框中勾選test method為:test_new_form
之后在新建的文件中補全對函數進行測試的代碼,在已經設定的類中添加測試函數的方法即可。
如圖所示:
form_display()函數測試結果如下:
遇到的問題:
在進行單元測試時發現如何測試都不能通過,但自己的程序卻能正常執行,網上查閱后發現要通過PyCharm對函數進行測試,必須按照特定的步驟方法,有些單元測試還要另外安裝特定的模塊,在按照正確的步驟進行后單元測試也可以正常運行
在一開始進行單元測試時,想當然的添加main函數,認為測試和正常的代碼執行無異,但嘗試了幾次且確認測試代碼沒有問題后測試還是無法正常開始,網上查閱后才發現Pycharm中進行單元測試時不會執行main()函數,在已經設定好的類中添加測試函數的方法即可。
②回歸測試
回歸測試是指修改了舊代碼后,重新進行測試已確認修改沒有引入新的錯誤或導致其他代碼產生錯誤的一種測試方法。具體流程就是在當前版本中對上一版本中測過的功能再重復測試一遍
在本次作業中,我在form_display()中新增了判斷語句以在運算結果為整數時執行save_to_file()函數,,為了確定功能添加后沒有產生新bug,再次對form_display的打印功能進行了測試。
Part 4.代碼提交
首先在git clone下來的文件路徑出右鍵,選擇Git bash here
,輸入命令行Git status
檢查倉庫狀態,如下圖所示:
接着輸入命令行Git push
便可以將代碼上傳至GitHub倉庫中,如下圖所示:
之后便可以看到GitHub上的倉庫中多出了與自己賬戶名相同的包含程序源碼的文件夾,點擊自己倉庫界面的New Pull Request
:
在彈出的新頁面中選擇Create pull request
即可成功將作業源碼提交,如下圖所示:
Part 5.感想
雖然這次作業占比較小,但卻也用了較長時間並且查閱網上得資料來完成,並且雖然使用了python來完成,卻沒有用到面向對象的思想通過創建類來實現程序,深刻體會到了目前能力的不足≡(▔﹏▔)≡。但通過完成這次作業,體會到了軟件設計到實現到測試到發布的大體流程,熟悉了各種工具的使用,並且在代碼實現過程中也鞏固了前段時間自學的Python,走了一遍較為完整的項目設計的流程,由衷地感受到了鄒欣老師所推崇的“做中學”的思想(ง •_•)ง。