Silent Reverie

Creating memories with the awesome stuff I've learnt.

Import This 与恺撒密码

| Comments

学过python脚本语言的人都知道python中有一个叫this的模块(module)。该模块只做了件很简单的事,打印一段字符串,内容是有关python语言的一些禅语(也可以称它为python哲学)。

要显示它很简单,在pythonREPLRead-Eval-Print Loop)中键入import this,可以看到如下字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

我想,这个模块应该是python里最简单、也最特殊的模块罢!出于好奇,查看了下这个模块的源码,如下所示:

python哲学 (this.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
s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""

d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print("".join([d.get(c, c) for c in s]))

呈现在眼前的不是一段平淡无奇的print语句,而采用了恺撒密码算法(Caesar cipher)。以下内容摘自维基百科,详见凯撒密码:

在密码学中,恺撒密码(或称恺撒加密、恺撒变换、变换加密)是一种最简单且最广为人知的加密技术。它是一种替换加密的技 术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是3的时候,所有的字母A将被替换成 D,B变成E,以此类推。这个加密方法是以恺撒的名字命名的,当年恺撒曾用此方法与其将军们进行联系。
恺撒密码通常被作为其他更复杂的加密方法中的一个步骤,例如维吉尼尔密码。恺撒密码还在现代的 ROT13系统中被应用。但是 和所有的利用字母表进行替换的加密技术一样,恺撒密码非常容易被破解,而且在实际应用中也无法保证通信安全。

可以看到,恺撒密码的原理很简单,对每个字母按照同一偏移量映射为别的字母,这样就完成了简单的加密。

看到this模块,我最大的感悟就是python语言优雅简洁的表达能力,我试过用JavaScript语言表达恺撒密码,实在为它感到汗颜。大概是因为JavaScript表达的废话太多,才催生了CoffeeScript这种比JavaScript更具表达力的语言吧。当然这得力于CoffeeScript大量借鉴python、ruby这种表达能力强的语言的语法才使然哩!

this.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
(function () {
  var lower   = 10,
      upper   = 17,
      current = 14;

  // 单位递增
  function next() {
    if (current < upper) {
      current += 1;
    } else {
      current = lower;
    }
    return current;
  }

  // 单位递减
  function prev() {
    if (current > lower) {
      current -= 1;
    } else {
      current = upper;
    }
    return current;
  }

  console.log(next()); // current = 15
  console.log(next()); // current = 16
  console.log(next()); // current = 17
  console.log(next()); // current = 10
}());

利用取余运算符%,我们可以将如上代码简化为如下(注意此时没有了if条件语句):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function () {
  var lower   = 10,
      upper   = 17,
      dist    = upper - lower + 1,
      current = 14;

  function next() {
    return (current = lower + (current - lower + 1 + dist) % dist);
  }

  function prev() {
    return (current = lower + (current - lower - 1 + dist) % dist);
  }

  console.log(prev()); // current = 13
  console.log(prev()); // current = 12
  console.log(prev()); // current = 11
  console.log(prev()); // current = 10
  console.log(prev()); // current = 17
}());

Comments