
程式中可能會發生的錯誤有三種,分別是語法錯誤 (syntax error) 、執行期間錯誤 (run-time error) 及語意錯誤 (semantic error)

直譯器 (interpreter) 會直接幫我們挑出語法錯誤,例如打錯識別字 (identifier) 名稱或是縮排錯誤等等。執行期間錯誤的話, Python 另有例外處理 (exception handling) 的機制,讓我們可以處理執行期間錯誤。三種錯誤中最麻煩的,就是語意錯誤了,因為有語意錯誤的程式,程式可以順利執行完畢,卻跑出錯誤的結果。
我們的 Encrypt 類別 (class) 目前正是碰到了發生語意錯誤的情況,這是說
y = a * x + b
m = y % n
r = m + diff
其中 a 與 b 若是 0 到 9 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,這是為什麼呢?嗯,好麻煩唷!這樣就得討論好多數學,打斷我們發展程式的腳步,所以我們不打算仔細討論這背後的數學理論,我們繼續測試,直接來找出哪些組合會得到錯誤的結果吧!
要知道哪些組合可能會發生錯誤,我們就得知道 a 與 b 的值,這不難,印出來就看得到了
| 001 | # 使用 randint() |
| 002 | import random |
| 003 | |
| 004 | # 定義 Encrypt 類別 |
| 005 | class Encrypt: |
| 006 | def __init__(self): |
| 007 | self.setcode() |
| 008 | |
| 009 | def setcode(self): |
| 010 | # 取得 a 、 b 值 |
| 011 | a = random.randint(0, 9) |
| 012 | print(a) # 印出 a |
| 013 | b = random.randint(0, 9) |
| 014 | print(b) # 印出 b |
| 015 | |
| 016 | # 利用公式建立密碼表 |
| 017 | self.code = "" |
| 018 | c = "a" |
| 019 | i = 0 |
| 020 | while i < 26: |
| 021 | x = c |
| 022 | y = ord(x) * a + b |
| 023 | m = y % 26 |
| 024 | self.code += chr(m + 97) |
| 025 | c = chr(ord(c) + 1) |
| 026 | i += 1 |
| 027 | |
| 028 | def getcode(self): |
| 029 | return self.code |
| 030 | |
| 031 | # 編碼的方法 |
| 032 | def toEncode(self, str): |
| 033 | pass |
| 034 | |
| 035 | # 解碼的方法 |
| 036 | def toDecode(self, str): |
| 037 | pass |
| 038 | |
| 039 | # 測試部分 |
| 040 | if __name__ == '__main__': |
| 041 | e = Encrypt() |
| 042 | print() |
| 043 | print(e.getcode()) |
| 044 | print() |
| 045 | |
| 046 | # 檔名: encrypt04.py |
| 047 | # 作者: Kaiching Chang |
| 048 | # 時間: July, 2014 |
這裡在 setcode() 加入印出 a 、 b 值的程式碼,因為 a 、 b 是屬於 setcode() 的區域變數 (local variable) ,因此只能在 setcode() 裡使用這兩個變數 (variable) 。
來測試看看囉

第一次, a 為 6 , b 為 5 就出問題,再多測試幾次看看

跑了幾次下來,似乎 a 為偶數或 0 就會跑出不符預期的結果,那我們就把 a 改成不是偶數或 0 好了!公式修改如下
if (a % 2) != 0 {
y = a * x + b
m = y % n
r = m + diff
}
這樣我們把發展中的版本 encrypt04.py 修改為 encrypt05.py ,其中需要修改的部份只有 setcode() ,如下
| 001 | # 使用 randint() |
| 002 | import random |
| 003 | |
| 004 | # 定義 Encrypt 類別 |
| 005 | class Encrypt: |
| 006 | def __init__(self): |
| 007 | self.setcode() |
| 008 | |
| 009 | def setcode(self): |
| 010 | # 取得 a 、 b 值 |
| 011 | a = 0 |
| 012 | b = 0 |
| 013 | while a % 2 == 0: |
| 014 | a = random.randint(0, 9) |
| 015 | b = random.randint(0, 9) |
| 016 | |
| 017 | # 利用公式建立密碼表 |
| 018 | self.code = "" |
| 019 | c = "a" |
| 020 | i = 0 |
| 021 | while i < 26: |
| 022 | x = c |
| 023 | y = ord(x) * a + b |
| 024 | m = y % 26 |
| 025 | self.code += chr(m + 97) |
| 026 | c = chr(ord(c) + 1) |
| 027 | i += 1 |
| 028 | |
| 029 | def getcode(self): |
| 030 | return self.code |
| 031 | |
| 032 | # 編碼的方法 |
| 033 | def toEncode(self, str): |
| 034 | pass |
| 035 | |
| 036 | # 解碼的方法 |
| 037 | def toDecode(self, str): |
| 038 | pass |
| 039 | |
| 040 | # 測試部分 |
| 041 | if __name__ == '__main__': |
| 042 | e = Encrypt() |
| 043 | print() |
| 044 | print(e.getcode()) |
| 045 | print() |
| 046 | |
| 047 | # 檔名: encrypt05.py |
| 048 | # 作者: Kaiching Chang |
| 049 | # 時間: July, 2014 |
重新執行測試如下

我們先把 a 與 b 的初值設為 0 ,然後以迴圈 (loop) 取得隨機的 a 、 b 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止。肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!
下面我們要開始實作處理編碼的部分,也就是實作 toEncode() 方法 (method) 。
中英文術語對照
| 語法錯誤 | syntax error |
| 執行期間錯誤 | run-time error |
| 語意錯誤 | semantic error |
| 直譯器 | interpreter |
| 識別字 | identifier |
| 例外處理 | exception handling |
| 類別 | class |
| 區域變數 | local variable |
| 變數 | variable |
| 迴圈 | loop |
| 方法 | method |
重點整理
- 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。
- 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。
- Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a 為 0 或偶數的情況,因此要避免 a 為 0 或偶數。
問題與討論
- 什麼是語法錯誤?試舉出語法錯誤的五個例子。
- 什麼是執行期間錯誤?想一想有哪些情況會發生執行期間錯誤?又要怎麼樣防範呢?
- 為什麼語意錯誤最麻煩呢?
- 我們是用什麼方式找出數學公式的錯誤呢?
練習
- 承接上一個單元的猜數字遊戲,將新程式寫在 exercise1701.py 中,在遊戲迴圈中用變數 times 計算使用者猜測的次數,並在遊戲結束時印出 times 。
- 承上題,將新程式寫在 exercise1702.py 中,設計一個檢查使用者輸入是否有重複數字的 find_number() 函數,結果回傳布林值。
the end
沒有留言:
張貼留言