
解碼 (decoding) 需要用到與編碼相同的轉換表格

所謂的解碼也就是將編碼 (encoding) 過的小寫英文字母回覆成原來的英文小寫字母,由上圖可以看出,我們儲存表格所用的字串 (string) ,恰巧依索引值 (index) 可推回原來的英文小寫字母,這是說,索引值 0 為 'q' ,所以是將原本的 'a' 變成 'q' ,因此,解碼就是依索引值重新加上 diff 即可。
實際上我們需要用到巢狀迴圈 (nested loop) ,也就是在迴圈 (loop) 中有其他的迴圈,對單一英文句子而言,我們需要一個迴圈判斷每個字元是否為英文小寫字母,若是英文小寫字母,我們就需要另一個迴圈找出對應的索引值。由這樣的概念設計的解碼方法 (method) toDecode() ,加進 encrypt07.py 中,如下
| 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 | # 暫存編碼結果的字串 |
| 035 | result = "" |
| 036 | |
| 037 | # 利用迴圈走完參數字串的所有字元 |
| 038 | for c in str: |
| 039 | # 判斷該字元是否為英文小寫字母 |
| 040 | # 若是英文小寫字母就進行編碼轉換 |
| 041 | c1 = ord(c) >= 97 |
| 042 | c2 = ord(c) <= 122 |
| 043 | if c1 and c2: |
| 044 | m = ord(c) - 97 |
| 045 | result += self.code[m] |
| 046 | else: |
| 047 | result += c |
| 048 | |
| 049 | # 結束回傳編碼過的字串 |
| 050 | return result |
| 051 | |
| 052 | # 解碼的方法 |
| 053 | def toDecode(self, str): |
| 054 | # 暫存解碼結果的字串 |
| 055 | result = "" |
| 056 | |
| 057 | i = 0 |
| 058 | # 第一層迴圈逐一取得每一個字元 |
| 059 | while i < len(str): |
| 060 | # 判斷該字元是否為英文小寫字母 |
| 061 | # 若是英文小寫字母就進行解碼轉換 |
| 062 | i1 = ord(str[i]) >= 97 |
| 063 | i2 = ord(str[i]) <= 122 |
| 064 | if i1 and i2: |
| 065 | j = 0 |
| 066 | # 第二層迴圈尋找該字元在密碼表中 |
| 067 | # 的索引值,加上 DIFF 就可轉換 |
| 068 | # 回原本的字元 |
| 069 | while j < len(self.code): |
| 070 | if str[i] == self.code[j]: |
| 071 | result += chr(j + 97) |
| 072 | j += 1 |
| 073 | else: |
| 074 | result += str[i] |
| 075 | |
| 076 | i += 1 |
| 077 | |
| 078 | # 結束回傳解碼過的字串 |
| 079 | return result |
| 080 | |
| 081 | # 測試部分 |
| 082 | if __name__ == '__main__': |
| 083 | e = Encrypt() |
| 084 | print() |
| 085 | print(e.getcode()) |
| 086 | s1 = "There is no spoon." |
| 087 | print("Input : " + s1) |
| 088 | s2 = e.toEncode(s1) |
| 089 | print("Encode: " + s2) |
| 090 | s3 = e.toDecode(s2) |
| 091 | print("Decode: " + s3) |
| 092 | print() |
| 093 | |
| 094 | # 檔名: encrypt07.py |
| 095 | # 作者: Kaiching Chang |
| 096 | # 時間: July, 2014 |
toDecode() 與 toEncode() 相似,同樣需要一個字串 (string) 當參數 (parameter) ,結果也回傳一個字串,解碼轉換則由 toDecode() 的巢狀迴圈部份來進行
| 057 | i = 0 |
| 058 | # 第一層迴圈逐一取得每一個字元 |
| 059 | while i < len(str): |
| 060 | # 判斷該字元是否為英文小寫字母 |
| 061 | # 若是英文小寫字母就進行解碼轉換 |
| 062 | i1 = ord(str[i]) >= 97 |
| 063 | i2 = ord(str[i]) <= 122 |
| 064 | if i1 and i2: |
| 065 | j = 0 |
| 066 | # 第二層迴圈尋找該字元在密碼表中 |
| 067 | # 的索引值,加上 DIFF 就可轉換 |
| 068 | # 回原本的字元 |
| 069 | while j < len(self.code): |
| 070 | if str[i] == self.code[j]: |
| 071 | result += chr(j + 97) |
| 072 | j += 1 |
| 073 | else: |
| 074 | result += str[i] |
| 075 | |
| 076 | i += 1 |
巢狀迴圈是迴圈中包含另一個迴圈,由於我們利用縮排的方式編輯程式碼,看起來內層迴圈像是凹陷進去的巢,故稱之為巢狀迴圈。
第一層迴圈,也就是外層迴圈會依序取得英文句子的每個字元,然後判斷是否為英文小寫字母,如果該字元是英文小寫字母,就會啟動第二層迴圈找出表格中對應的索引值出來。控制變數 i 在最後會遞增,接著判斷下一個字元。
注意,這個巢狀迴圈用了兩個 while 、兩個 if ,依順序 while 、 if 、 while 、 if ,這是我們打算讓程式執行的順序,若是沒有依照這樣的順序,程式可能會跑出無法預期的結果。
Encrypt 類別到這邊大致已開發完成,最後的測試部分也加入了解碼的相關程式碼
| 081 | # 測試部分 |
| 082 | if __name__ == '__main__': |
| 083 | e = Encrypt() |
| 084 | print() |
| 085 | print(e.getcode()) |
| 086 | s1 = "There is no spoon." |
| 087 | print("Input : " + s1) |
| 088 | s2 = e.toEncode(s1) |
| 089 | print("Encode: " + s2) |
| 090 | s3 = e.toDecode(s2) |
| 091 | print("Decode: " + s3) |
| 092 | print() |
執行結果如下

結果如預期,下面我們進入 GUI 之前先岔開討論一個問題,就是我們程式雖然順利跑出結果,可是一點也不 Python 說,因此我們要先來重構 (refactoring) 程式碼。
中英文術語對照
| 解碼 | decoding |
| 編碼 | encoding |
| 字串 | string |
| 索引值 | index |
| 巢狀迴圈 | nested loop |
| 迴圈 | loop |
| 方法 | method |
| 字串 | string |
| 參數 | parameter |
| 重構 | refactoring |
重點整理
- 解碼與編碼用到相同的轉換表格,因為索引值 0 等於 'a' ,索引值 1 等於 'b' ,餘下可類推。
- 巢狀迴圈是指迴圈中有其他迴圈,因為用縮排的方式編排程式碼,凹陷下去的地方看起來像鳥巢,故有此名。
- 解碼利用巢狀迴圈進行,第一層迴圈逐一取得每一個字元,第二層迴圈尋找該字元在密碼表中的索引值。
問題與討論
- 為什麼編碼跟解碼可以用一樣的轉換表格?
- 可以不用巢狀迴圈來解碼嗎?
- 巢狀迴圈可以用一樣名稱的控制變數嗎?
練習
- 承接上一個單元的猜數字遊戲,將新程式寫在 exercise1901.py 中,修正第一位數可能為 0 的情況。
- 承上題,將猜數字遊戲改用類別 GuessGame 設計,答案用 answer 屬性儲存,另外設置 times 、 a 、 b 等屬性,把設定以上屬性的程式內容放在 set_game() 方法裡。
the end
沒有留言:
張貼留言