Pythonの組み込み実装のROT13を読む。
アルファベットを任意の文字数ずらした暗号をシーザー暗号という。
シーザー暗号の中で13文字ずらしたものをとくにROT13と呼ぶ。
PythonではこのROT13が言語に組み込み実装として存在しており、thisモジュールはこのROT13でエンコードされている。
>>> print "hogefuga".decode("rot13") ubtrshtn >>> print "hogefuga".encode("rot13") ubtrshtn >>>
自明ではあるが上の出力でROT13においてはデコードとエンコードが同じ処理であることも確認できる。
組み込み実装として用意されているROT13のソースコードはthis.pyを見れば確認できる。
https://github.com/python/cpython/blob/master/Lib/this.py
23 d = {} 24 for c in (65, 97): 25 for i in range(26): 26 d[chr(i+c)] = chr((i+13) % 26 + c) 27 28 print "".join([d.get(c, c) for c in s])
23行目で空の辞書オブジェクトを生成している。
24行目ではついつい、
for c in (65, 97):
の部分を
for c in range(65, 97):
に空目してしまいがちだが
range()関数は使っていないので注意が必要。
65と97はASCII文字コードにおけるA
とa
である。
chr()関数はASCII文字コードにおける整数を文字に変換する関数で、逆の処理をするのがord()関数。
- chr()
>>> for c in (65, 97): ... for i in range(26): ... print chr(i+c), ... A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z >>>
- ord()
>>> for c in ('A', 'a'): ... for i in range(26): ... print ord(c)+i, ... 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 >>>
26行目の
d[chr(i+c)] = chr((i+13) % 26 + c)
は
d['A'] = 'N'
d['B'] = 'O'
d['C'] = 'P'
...
のようなことをしており{'A':'N', 'B':'O', 'C':'P' ...(以下略)}という辞書を生成している。
13というのがずらす文字数で0から25の整数である変数iに13を足したものの剰余をとり65もしくは97を足してchr()関数を実行することで、うまくシーザー暗号を表現している。
この13をnにすることでより汎用性の高いプログラムになりそうである。
最後のprint "".join([d.get(c, c) for c in s])
だが、
"".join()はリストから文字列への変換操作である。
>>> "".join(['h', 'o', 'g', 'e']) 'hoge'
リストは内包表記を使っている。d.get(c, c)
はd[c]があればその値を返しなければcをそのまま返すという意味である。
>>> d = {'A':'N', 'B':'O',} >>> d.get('A') 'N' >>> d.get('N') >>> d.get('N', 'N') 'N'
引数に文字列sと整数nをとり、文字列sをn文字シフトした文字列を返す関数caesar()は次のように書ける。
def caesar(s, n): d = {} for c in (65, 97): for i in range(26): d[chr(i+c)] = chr((i+n) % 26 + c) return "".join([d.get(c, c) for c in s])
例えばAOJのこの問題(http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0017)の仕様に合わせるのであれば、
こんな風に使える。