Python 入門指南 5.0

單元 25 - 修正後的數學公式

~~學習進度表~~

語意錯誤 (semantic error) 是程式 (program) 三種錯誤中最麻煩的一種,因為程式可以順利跑出結果,卻是錯誤 (error) 的結果

Semantic Errors
→ programmer

對語意錯誤除錯沒有什麼快速的有效方式,必須重新檢視程式中的每一個計算步驟,然後去找出是哪一個步驟導致發生錯誤,也就是說,某個步驟計算出的值導致語意錯誤。

重新檢視 Encrypt 類別 (class) ,有可能發生錯誤的不意外會是新加入的程式碼,這就是說新的產生 ab 值的機制可能會產生錯誤

19
20
        a = randint(0, 9)
        b = randint(0, 9)

回到原本設想的公式

  
  
  
y = a * x + b
m = y % n
r = m + diff

其中 ab 若是 09 隨機整數,有些組合成立,可以得到正確結果,有些組合卻會得到錯誤的結果,要知道哪些組合可能會發生錯誤,我們就得知道 ab 的值,這不難,印出來就看得到了

 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() 加入印出 ab 值的程式碼,因為 ab 是屬於 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
 
$

第一次, a4b9 就出問題,再多測試幾次看看

$ 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 時才會結束取 ab 值的迴圈 (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
 
$

我們先把 ab 的初值設為 0 ,然後以迴圈取得隨機的 ab 值,用 a 除以 2 的餘數為 0 當作迴圈的執行條件,因此當 a 等於 0 或偶數時,迴圈就會持續進行,直到 a 不為 0 或偶數為止。肉眼檢查下,似乎只要 a 為奇數,計算出的結果就不會有問題囉!

下面我們要開始實作處理編碼的部分,也就是實作 toEncode() 方法 (method) 。

中英文術語對照
類別class
錯誤error
區域變數local variable
迴圈loop
方法method
程式program
語意錯誤semantic error
變數variable
重點整理
1. 程式中可能會發生語法錯誤、執行期間錯誤或語意錯誤,編譯器會直接挑出語法錯誤,而執行期間錯誤可用例外處理機制來防範。
2. 發生語意錯誤的程式可順利執行跑出結果,可是結果不符合預期。
3. Encrypt 的數學公式經過再三的測試檢驗,發現問題出在 a0 或偶數的情況,因此要避免 a0 或偶數。
問題與討論
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 中,加入讓使用者設定骰子數量的機制。 參考程式碼

上一頁 單元 24 - 繼續測試
回 Python 入門指南 5.0 首頁
下一頁 單元 26 - 編碼
回 Python 教材首頁
回程式語言教材首頁