Python 入門指南 5.0
單元 25 - 修正後的數學公式
語意錯誤 (semantic error) 是程式 (program) 三種錯誤中最麻煩的一種,因為程式可以順利跑出結果,卻是錯誤 (error) 的結果
→ programmer
對語意錯誤除錯沒有什麼快速的有效方式,必須重新檢視程式中的每一個計算步驟,然後去找出是哪一個步驟導致發生錯誤,也就是說,某個步驟計算出的值導致語意錯誤。
重新檢視 Encrypt 類別 (class) ,有可能發生錯誤的不意外會是新加入的程式碼,這就是說新的產生 a 、 b 值的機制可能會產生錯誤
19 20 | a = randint(0, 9)
b = randint(0, 9)
|
回到原本設想的公式
y = a * x + b
m = y % n
r = m + diff
|
其中 a 與 b 若是 0 到 9 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,要知道哪些組合可能會發生錯誤,我們就得知道 a 與 b 的值,這不難,印出來就看得到了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | # 使用標準程式庫中 random 的 randint()
from random import randint
# 定義 Encrypt 類別
class Encrypt:
# 定義建構子
def __init__(self):
# 建立密碼表
self.set_code()
# _code 的 getter
@property
def code(self):
return self.__code
# _code 的 setter
def set_code(self):
# 取得 a 、 b 值
a = randint(0, 9)
print(a) # 印出 a
b = randint(0, 9)
print(b) # 印出 b
# 利用公式建立密碼表
self.__code = ""
c = "a"
i = 0
while i < 26:
x = c
y = ord(x) * a + b
m = y % 26
self.__code += chr(m + 97)
c = chr(ord(c) + 1)
i += 1
# 編碼的方法
def toEncode(self, str):
pass
# 解碼的方法
def toDecode(self, str):
pass
# 測試部分
if __name__ == '__main__':
e = Encrypt()
print()
print(e.code)
print()
# 檔名: encrypt04.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
這裡在 setcode() 加入印出 a 、 b 值的程式碼,因為 a 、 b 是屬於 setcode() 的區域變數 (local variable) ,因此只能在 setcode() 裡使用這兩個變數 (variable)
19 20 21 22 | a = randint(0, 9)
print(a) # 印出 a
b = randint(0, 9)
print(b) # 印出 b
|
來測試看看囉
$ python encrypt04.py |
4 |
9 |
hlptxbfjnrvzdhlptxbfjnrvzd |
$ |
第一次, a 為 4 , b 為 9 就出問題,再多測試幾次看看
$ python encrypt04.py |
6 |
2 |
msyekqwciouagmsyekqwciouag |
$ python encrypt04.py |
9 |
0 |
pyhqzirajsbktcludmvenwfoxg |
$ python encrypt04.py |
0 |
8 |
iiiiiiiiiiiiiiiiiiiiiiiiii |
$ |
跑了幾次下來,似乎 a 為偶數或 0 就會跑出不符預期的結果,那我們就把 a 改成不是偶數或 0 好了!公式修改如下
if (a % 2) != 0 {
y = a * x + b
m = y % n
r = m + diff
}
|
這樣我們把發展中的版本 encrypt04.py 修改為 encrypt05.py ,其中需要修改的部份只有 setcode() ,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | # 使用標準程式庫中 random 的 randint()
from random import randint
# 定義 Encrypt 類別
class Encrypt:
# 定義建構子
def __init__(self):
# 建立密碼表
self.set_code()
# _code 的 getter
@property
def code(self):
return self.__code
# _code 的 setter
def set_code(self):
# 取得 a 、 b 值
a = 0
b = 0
while a % 2 == 0:
a = randint(0, 9)
b = randint(0, 9)
# 利用公式建立密碼表
self.__code = ""
c = "a"
i = 0
while i < 26:
x = c
y = ord(x) * a + b
m = y % 26
self.__code += chr(m + 97)
c = chr(ord(c) + 1)
i += 1
# 編碼的方法
def toEncode(self, str):
pass
# 解碼的方法
def toDecode(self, str):
pass
# 測試部分
if __name__ == '__main__':
e = Encrypt()
print()
print(e.code)
print()
# 檔名: encrypt05.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
這裡,只有當 a 除以 2 不為 0 時才會結束取 a 、 b 值的迴圈 (loop)
19 20 21 22 | a = 0
b = 0
while a % 2 == 0:
a = randint(0, 9)
b = randint(0, 9)
|
重新執行測試如下
$ python encrypt05.py |
zejotydinsxchmrwbglqvafkpu |
$ python encrypt05.py |
yzabcdefghijklmnopqrstuvwx |
$ python encrypt05.py |
kryfmtahovcjqxelszgnubipwd |
$ python encrypt05.py |
tuvwxyzabcdefghijklmnopqrs |
$ |
我們先把 a 與 b 的初值設為 0 ,然後以迴圈取得隨機的 a 、 b 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止。肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!
下面我們要開始實作處理編碼的部分,也就是實作 toEncode() 方法 (method) 。
中英文術語對照 | |
---|---|
類別 | class |
錯誤 | error |
區域變數 | local variable |
迴圈 | loop |
方法 | method |
程式 | program |
語意錯誤 | semantic error |
變數 | variable |
重點整理 |
---|
1. 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。 |
2. 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。 |
3. Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a 為 0 或偶數的情況,因此要避免 a 為 0 或偶數。 |
問題與討論 |
---|
1. 什麼是語法錯誤?試舉出語法錯誤的五個例子。 |
2. 什麼是執行期間錯誤?想一想有哪些情況會發生執行期間錯誤?又要怎麼樣防範呢? |
3. 為什麼語意錯誤最麻煩呢? |
4. 我們是用什麼方式找出數學公式的錯誤呢? |
練習 |
---|
1. 承接單元 23 練習的 DiceUser 類別,將新程式寫在 exercise2501.py 中, 加入玩家資產屬性 asset ,並在擲骰子方法中加入賭注參數 bet ,當使用者呼叫擲骰子方法,就將玩家資產減掉賭注。 參考程式碼 |
2. 承上題,將新程式寫在 exercise2502.py 中,繼續擴充單元 23 練習的骰子遊戲類別 DiceGame ,建構子加入資產 asset 及賭注 bet 兩個參數,並在遊戲進行方法 playgame() 加入賭注與玩家資產的計算。 參考程式碼 |
3. 承上題,將新程式寫在 exercise2503.py 中,重新設計 DiceUser 類別,移除擲骰子方法中的賭注參數,將賭注計算部分移到遊戲執行的地方。 參考程式碼 |
4. 承上題,將新程式寫在 exercise2504.py 中,重新設計 DiceGame 類別,將資產分為莊家資產與玩家資產。 參考程式碼 |
5. 承上題,將新程式寫在 exercise2505.py 中,重新設計 dice_game() 函數,加入分配賭注的部分。 參考程式碼 |
6. 承上題,將新程式寫在 exercise2506.py 中,加入出局機制。 參考程式碼 |
7. 承上題,將新程式寫在 exercise2507.py 中,重新設計命令列執行方式,讓使用者以自訂暱稱加入。 參考程式碼 |
8. 承上題,將新程式寫在 exercise2508.py 中,加入讓使用者選擇是否要當莊家。 參考程式碼 |
9. 承上題,將新程式寫在 exercise2509.py 中,加入讓使用者設定遊戲人數的機制。 參考程式碼 |
10. 承上題,將新程式寫在 exercise2510.py 中,加入讓使用者設定骰子數量的機制。 參考程式碼 |