Python 入門指南 5.0
單元 23 - 實作 setcode()
setcode() 的工作就是建立密碼表,我們需要的密碼表就是一個攪亂順序的英文字母表
⇓
0 | 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 |
q | z | i | r | a | j | s | b | k | t | c | l | u | d | m | v | e | n | w | f | o | x | g | p | y | h |
先來溫習一下用來編碼的數學公式
y = a * x + b
m = y % n
r = m + diff
|
a 與 b 預定是 0 到 9 之間的隨機整數,要取得隨機整數的話,標準程式庫 (standard library) 有程式可用,倒是現在先不用急著一次到位,我們先把 a 設定成 3 , b 設定成 5 測試看看好了,如此 setcode() 的實作如下
# _code 的 setter
def set_code(self):
# 取得 a 、 b 值
a = 3
b = 5
# 利用公式建立密碼表
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
|
__code 屬性 (attribute) 的初值設定為空字串 (empty string)
| self.__code = ""
|
下面設定兩個控制變數 (control variable) , c 為記錄目前處理的字母, i 則是記錄迴圈 (loop) 進行的次數
c = "a"
i = 0
|
因為英文字母表的小寫字母共有 26 個,因此公式用迴圈跑了 26 次
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
|
這裡把 c 指派給 x 後,因為 c 是字串 (string) ,使得 x 也是字串,由於字串做乘法跟加法為複製字串,所以用內建函數 (function) ord() 將 x 轉換成 Unicode 編碼值,然後才進行計算
| y = ord(x) * a + b
|
取得餘數 m 後,再利用內建函數 chr() 將 Unicode 編碼值轉換為單一字母的字串,這裡 += 會把單一字母的字串附加到 __code 的最後
| self.__code += chr(m + 97)
|
咦?不是說字串是不可變的 (immutable) 嗎?不可變的複合物件無法更改裡頭的元素值,那這裡為什麼可以這樣寫呢?原因很簡單,這裡是重新指派,每一次 += 都是把新的字串物件給 __code ,所以沒有涉及到更改內容的問題。
最後,兩個控制變數都要遞增
c = chr(ord(c) + 1)
i += 1
|
我們把目前的版本放在 encrypt02.py 裡,如下
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 | # 定義 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 = 3
b = 5
# 利用公式建立密碼表
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()
# 檔名: encrypt02.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
測試部分就是印出密碼表,執行結果如下
$ python encrypt02.py |
knqtwzcfiloruxadgjmpsvybeh |
$ |
結果如預期,一個英文小寫字母恰恰好對應到另外一個英文小寫字母,這樣我們的公式就 ok 了嗎? a 等於 3 跟 b 等於 5 是沒問題的,可是我們希望 a 與 b 可以是 0 到 9 之間的任意整數,這樣就有 100 種組合說,所以我們接下來要繼續測試,看看是不是每一種組合都 ok 囉!
中英文術語對照 | |
---|---|
屬性 | attribute |
控制變數 | control variable |
函數 | function |
不可變的 | immutable |
迴圈 | loop |
空字串 | null string |
標準程式庫 | standard library |
字串 | string |
重點整理 |
---|
1. 實作 setcode() 是先將公式中的 a 設成 3 , b 設成 5 ,然後用迴圈利用兩個控制變數 i 與 c 逐一計算每個字元。 |
2. 內建函數 ord() 可將字串轉換成 Unicode 編碼數字,而 chr() 將 Unicode 編碼數字轉換回字串。 |
問題與討論 |
---|
1. 為什麼不把建立對換表格直接寫在 __init__() 裡就好了? |
2. 為什麼字串變數用 += 可以把字元附加到字串物件的最後? |
練習 |
---|
1. 寫一個程式 exercise2301.py ,利用標準程式庫 random 的 randint() 及上一個單元發展的 higher() 在命令列設計模擬擲骰子遊戲, 需要有莊家及玩家,然後印出擲出點數及輸贏。 參考程式碼 |
2. 承上題,將新程式寫在 exercise2302.py 中,加入判斷平手的機制。 參考程式碼 |
3. 承上題,將新程式寫在 exercise2303.py 中,設計骰子類別 Dice ,利用 point 屬性記錄骰子點數。 參考程式碼 |
4. 承上題,將新程式寫在 exercise2304.py 中,設計骰子玩家類別 DiceUser 的初始版本,利用 name 屬性記錄玩家名稱。 參考程式碼 |
5. 承上題,將新程式寫在 exercise2305.py 中,繼續擴充骰子玩家類別,利用屬性 dice1 到 dice4 記錄最多四顆骰子點數,另外利用屬性 total_points 記錄終點數, dice_state 記錄骰子數量。 參考程式碼 |
6. 承上題,將新程式寫在 exercise2306.py 中,繼續擴充骰子玩家類別,定義 roll1Dice() 到 roll4Dice() 方法,分別進行擲出一顆到四顆骰子。 參考程式碼 |
7. 承上題,將新程式寫在 exercise2307.py 中,設計 DiceGame 類別,建構子以 names 與 number 當參數, names 預計接收玩家名稱的字串串列, number 則是骰子數量,另外用迴圈建立 DiceUser 的串列屬性 players 。 參考程式碼 |
8. 承上題,將新程式寫在 exercise2308.py 中,替 DiceGame 類別繼續設計 playgame() 方法,將遊戲進行方式的程式碼移到這裡。 參考程式碼 |
9. 承上題,將新程式寫在 exercise2309.py 中,定義命令列執行方式的 dice_game() 函數。 參考程式碼 |
10. 承上題,將新程式寫在 exercise2310.py 中,簡化 dice_game() 函數,用進行十萬次模擬。 參考程式碼 |