1. 程式人生 > 實用技巧 >2019ccpc哈爾濱(補題)

2019ccpc哈爾濱(補題)

作業要求參見:https://edu.cnblogs.com/campus/nenu/2020Fall/homework/11245

結對物件:蔣慧

說明:程式語言 python 程式設計工具 pycharm

要求1

(1) 給出每個功能的重點、難點、程式設計收穫。

功能1. 四則運算

支援出題4個數的四則運算題目,所有題目要求作者有能力正確回答 (提示:1/3 != 0.33333333333333333333333333333333,而是無限長)。為了快出成果,你快速造個控制檯的版本,包括以後改版成更優秀介面的核心功能,並考慮到擴充套件。

> f4

1+2*3+4=

?11

答對啦,你真是個天才!

1+2*3+5=

?11

再想想吧,答案似乎是12喔!

1+2/4-5=

?-3.5

答對啦,你真是個天才!

...(一共20道題)

你一共答對4道題,共20道題。

功能1重點:生成表示式、表示式計算、輸入結果的判斷及處理、按型別(整數、浮點數、帶分數)顯示正確結果形式、滿足格式要求。

功能1難點:按型別(整數、浮點數、帶分數)顯示正確結果形式。

執行截圖:

重要程式碼片段:

將每個表示式的輸入結果與正確結果比較,並給出相應的提示資訊。

ret = []  # 存放表示式
for i in range(20):
    ret.append(expression())
n = 0
for f1 in ret:
    
print(f1+'=') print('?', end="") result = Fraction(eval(input())).limit_denominator() # 輸入的值轉分數 answer = Fraction(eval(f1)).limit_denominator() # eval()計算表示式的值並轉分數 if result == answer: n += 1 print('答對啦,你真是個天才!') else: print('再想想吧,答案似乎是{}喔!'.format(dealAnswer(answer)))
print('你一共答對%d道題,共20道題。' % n)

輸出正確結果的函式:dealAnswer()

def dealAnswer(fraction):
    n1 = fraction.numerator
    n2 = fraction.denominator
    if len(str(float(fraction))) > 15:  # 如果正確結果無限,則以帶分數形式輸出
        sub = int(n1 / n2)
        n1 = n1 % n2
        if sub == 0:
            return '%d/%d' % (n1, n2)  # 如果帶分數的sub部分為0,則不輸出這部分
        else:
            return '%d %d/%d' % (sub, n1, n2)
    elif n2 == 1:  # 結果是整數,原樣輸出
        return n1
    else:
        return float(fraction)  # 結果是有限小數,原樣輸出

生成表示式函式:expression()

# 生成表示式
def expression():
    op = ['+', '-', '*', '/']
    num1 = random.randint(1, 10)
    num2 = random.randint(1, 10)
    num3 = random.randint(1, 10)
    num4 = random.randint(1, 10)
    op1 = random.choice(op)
    op2 = random.choice(op)
    op3 = random.choice(op)
    formula = str(num1) + str(op1) + str(num2) + str(op2) + str(num3) + str(op3) + str(num4)
    return formula

程式設計收穫:

1.eval()其中一個功能是可以計算表示式的值。

2.Fraction(eval(f1)).limit_denominator() 將表示式f1的值轉為分數。

3.str.format格式化輸出字串的函式(str.format),基本語法是通過{}和:代替以前的%。

4.fraction.numerator取分數的分子,fraction.denominator取分數的分母。

5.if len(str(float(fraction))) > 15: 表示式結果是無限小數,則用帶分數形式輸出正確結果。

6.需要通過不斷測試去發現問題,解決問題。

7.兩個人合作程式設計思路會比較多,解決問題也比較快。

功能2. 支援括號

老師看了你的表演,大大讚賞了你。然後她說,"你的題庫裡怎麼都是沒有括號的題呢,我記得你當初括號就掌握得不好啊。"你的臉紅了,對老師說,"給我2個小時時間,我給你一個新版本,有括號的。"

你拿出筆記本,偷偷微信你們《構建之法》班的學霸,她說肯定能行,但是細節訊號不好你聽不清,只捕捉到隱約幾個詞"逆波蘭""字尾表示式""堆疊""我看好你""資料結構"。

兩小時後,在老師面前你在控制檯下輸入f4,然後回車。

> f4

1+2*(3+4)=

?15

答對啦,你真是個天才!

(1+2)*3+5=

?11

再想想吧,答案似乎是14喔!

((1/2)-4)*5=

?-17.5

答對啦,你真是個天才!

...(一共20道題)

你一共答對4道題,共20道題。

功能2重點:考慮括號可新增位置、如何表示是否生成括號、成對出現情況。

功能2難點:括號成對出現情況

執行截圖:

重要程式碼片段:

括號生成:

    bra = ['(', '', ')']  
    # 0:生成左括號 1:不生成括號 2:生成右括號
    left1 = random.randint(0, 1)
    left2 = random.randint(0, 1)
    left3 = random.randint(0, 1)
    right1 = random.randint(1, 2)
    right2 = random.randint(1, 2)
    right3 = random.randint(1, 2)
    if left1 == 0:          
        left2 = 1
        left3 = 1
        if right1 == 2:
            right2 = 1
            right3 = 1
        else:
            right2 = 2
            right3 = 1
    else:
        if left2 == 0:
            left3 = 1
            right1 = 1
            if right2 == 2:
                right3 = 1
            else:
                right3 = 2
        else:
            left3 = 0
            right1 = 1
            right2 = 1
            right3 = 2

程式設計收穫:

1.用隨機數字代表括號是否產生(1不產生,0/2產生)以及產生括號的位置(0產生左括號,2產生右括號),比較方便。

2.程式碼實現中沒有用題目中提到的"逆波蘭""字尾表示式""堆疊"解決括號匹配問題,而是用if else分情況討論的。

功能3. 限定題目數量,"精美"列印輸出,避免重複

"就這點兒題,像你當年做得那麼快,一會兒就完成啦!"老師說,"另外,我想打印出來,上課也不能帶臺機器。又另外,你把答案也打出來唄,我把答案剪掉,題目給學生做。"

一看需求這麼多,你生怕她會說,"這都是很簡單的功能,你一定能完成吧"。你知道如果承諾今晚,明早交工的時候她一定想出了更多可怕的需求,你趕緊說,"老師我現在就做。"

你忘記怎麼呼叫印表機了,就把答案與題目橫向對齊,輸出在檔案的右邊。告訴老師txt檔案可以用WORD開啟,也能列印。她滿意而意味深長地笑了,表示同意。

你輸入命令執行的時候,腦袋比手指頭還疼。

>f4 -c 3

1+2*(3+4)= 15

(1+2)*3+5= 14

((1/2)-4)*5= 17.5

你指著螢幕對老師說,">"是提示符,實際執行時可能是"C:\Users\Young>","f4"是程式的名字,它真正的名字是"f4.exe","-c 3"是命令列引數。按下回車,此時程式開始執行。"-c"這個引數後面輸入多少,就生成多少題目。老師輸入

>f4 -c -200

題目數量必須是 正整數。

>f4 -c 3.5

題目數量必須是 正整數。

>f4 -c test

題目數量必須是 正整數。

老師欣慰的笑了。你怔怔地看著她,心下懷疑她真的是教初中數學的那位麼。

老師在你的指導下試用了一下,打印出來的題目堪稱精美。老師讓你做一次試試看能得多少分。你不敢借口四則運算忘光了,只好提起笑來開始答。剛做一半,你發現有幾道題目是相同的,心中暗罵隨機數不靠譜。又有幾道雖然不完全相同,但是可以通過交換律、結合律、分配律變換為同一道題。比如

1*2+3*2

2*(1+3)+0

功能3重點:如何通過命令列輸入引數(-c和限定表示式個數num)、判斷輸入引數num是否正整數、表示式結果輸出格式控制、判斷表示式是否重複、將表示式及答案寫入檔案。

功能3難點:判斷表示式是否重複

執行截圖:

通過命令列輸入引數並顯示錶達式和答案截圖:

通過命令列輸入的題目數量不是正整數截圖:

通過命令列輸入引數並將表示式和答案寫入檔案截圖:

重要程式碼片段:

通過命令列輸引數並判斷題目數量是否正整數:

def main(argv):
    if sys.argv[1] == "-c":
        if sys.argv[2].isnumeric():  # str.isnumeric()判斷是否只有數字
            expToFile(int(sys.argv[2]))
        else:
            print("題目數量必須是 正整數。")
    else:
        sys.exit()

在控制檯輸出限定表示式和答案:

    for f1 in ret:
        answer = Fraction(eval(f1)).limit_denominator()  # eval()計算表示式的值並轉分數
        print('%-15s%-15s' % (f1+'=', dealAnswer(answer)))

將限定表示式和答案寫入檔案:

    n = 0
    for f1 in ret:
        n = n+1
        if n == 1:
            if os.path.exists("題目輸出.txt"):
                os.remove("題目輸出.txt")
        answer = Fraction(eval(f1)).limit_denominator()  # eval()計算表示式的值並轉分數
        formula = f1 + '=' + "\t\t" + str(dealAnswer(answer))  # 將表示式、空格、答案連線
        writeFile(formula)
def writeFile(formula):
    with open("題目輸出.txt", 'a+') as file:
        file.write(formula)
        file.write('\n')
        return
    print("檔案寫入失敗!")

程式設計收穫:

1.通過命令列傳引數可以用Python 提供的getopt 模組,而我們用的是sys 的 sys.argv。sys.argv 是命令列引數列表,len(sys.argv) 是命令列引數個數,sys.argv[0] 表示指令碼名是第一個引數,sys.argv[1]是第二個引數,sys.argv[2]是第三個引數。需要注意命令列輸入的引數是字串型別,如果是數字需轉int型。

2.對傳入的引數判斷是否是正整數,可以採用str.isnumeric()判斷是否只有數字。

3.通過調整'%-15s%-15s'中的數值來調整在控制檯輸出的表示式和答案之間的距離。

4.將表示式和答案寫入檔案:題目輸出.txt,每次執行命令生成第一個表示式後如果檔案存在,則移除,新建,寫入。

5.關於表示式重複問題,雖然我們還沒有進行編碼實現,但是有進行探討、考慮解題思路。首先,在題目的解讀上最開始有一些疑問,在作業要求中關於表示式重複問題給出了一個例子,1*2+3*2和2*(1+3)+0這兩個式子是屬於重複題,但是我和蔣慧同學認為不是重複題,因為要求中提到能將表示式通過交換律、結合律、分配律轉變為同一題的是重複題。接著我們查閱了加法、乘法交換律及結合律,乘法分配律,發現第二個式子有+0還涉及到計算問題,後來猜想可能是因為題目是要求四個數的四則運算,在保證結果一致的情況下才加0的,只有0比較特殊。將猜想、疑問向老師、師兄請教之後,終於解開疑問,是因為面向的使用者是“小學生”,所以加0和乘0認為是重複的。其次,是思路上,通過查閱資料,總結了一個思路大致如下:生成表示式1——轉為逆波蘭式——存入列表list1——(遍歷列表並規範化)形成規範化的二叉樹——將規範後的二叉樹轉為逆波蘭形式字串——存入列表list2,表示式2同理,但在存入列表list2之前先和此列表中的所有元素比較,如果列表list2已有,則捨棄此表示式,如果列表list2沒有,則加入列表。

(2)結對程式設計的體會

通過和蔣慧同學結對程式設計,認識到了結對程式設計有很多好處,比如兩個人結對程式設計會產生更多的思路,不會侷限在某一方面。遇到問題時,兩個人可以一起討論,一起解決,在程式設計的過程中也充分認識到了合作的重要性。總的來說,結對程式設計相比兩個人獨立程式設計會更節省時間,效率更高。另外,通過結對程式設計我和蔣慧同學相比之前也更加默契。

(3)至少5項在編碼、爭論、複審等活動中花費時間較長,給你較大收穫的事件。(10分)

第一,在功能3的表示式重複問題上花費時間比較多。從討論、理解題目,到查閱資料、和同學交流之後有了一些思路,雖然還未實現程式設計,但收穫很多,之後也會嘗試程式設計實現,不斷完善思路。

第二,在編碼實現各個功能過程中,格式控制也花費了較多的時間。最開始不滿足要求,然後會根據需求編寫、修改程式碼,進行除錯執行,再發現問題,查閱資料,解決問題,最後確定最終程式碼,滿足格式要求。

第三,在初次編寫完3個功能後,有對程式碼進行復審,這一項花費時間比較多。初次編寫各個功能的過程中,存在一些未解決的問題或者不符合功能要求的問題。複審程式碼是非常必要的,複審程式碼主要是為了完善每個功能程式碼並整理、確定每個功能的最終程式碼,另外還要儘可能的加上詳細的程式碼註釋,方便讀者理解程式碼。在複審的過程中還可能會發現一些bug、漏銅等 ,可以有效減少程式碼錯誤、縮排等格式問題,從而更滿足功能要求。

第四,編碼過程中查閱Python基本語法這部分也佔用了不少時間。由於對Python語言的不熟悉,尤其是Python中的一些函式、方法的使用,導致需要花費一定時間去查閱並應用。一方面可以對自己已掌握的函式、方法有更深、更全的理解,另一方面,完善自己的函式庫。在這個過程中,發現自己對於一些函數出現了記憶偏差問題,導致給編碼實現帶來了一定的困難。所以對已經用過、學過的語法要不斷去鞏固,加深記憶。

第五,在編碼過程中,通過命令列傳引數在前兩週的作業中已經涉及過,但是在編寫功能3的過程中,還是不能很快地編寫實現,邏輯上認為沒問題,實踐中會出現意向不到的問題。認識到程式碼放到具體問題中,還是需要進行調整,甚至是細微的,通過不斷除錯去解決問題。

要求2給出結對開發的截圖證據,要求截圖能夠證明你們是在結對程式設計。 (5分)

要求3使用coding.net做版本控制。checkin 前要求清理 臨時檔案、可執行程式,通常執行 build-clean可以達到效果。(25分)

雖然只有5分,但此題如果做錯,因為教師得不到你的程式碼,所以會導致“功能實現”為負分。
程式碼要求在 coding.net 做版本控制。要求push&pull時使用git客戶端,不允許使用web頁面。
要求頻繁checkin。要求在PSP中記錄的每次離開鍵盤30分鐘以上,需要checkin。如果你持續長達4小時不離開鍵盤,教師要求展示此項能力。量化的可考核的指標要求: 每個功能,要求有4次以上 checkin,展示(1)逐步實現功能和(2)修復bug時的過程; 每次checkin都要求可以成功編譯。
推薦git客戶端tortoisgit。
推薦先pull [https://github.com/weijunying2019102969/novelsfortest.git],裡面有測試用例。

coding.net程式碼地址:https://e.coding.net/zhangwenyan1/Arithmetic/arithmetic.git

未完成