Rot13和取模的使用
我最近在读艾伦·B·道尼的《Think Python》,书中有一个练习(8.12),要求创建一个ROT13函数。我做了一个,但只部分成功,因为我在处理大写字母时遇到了困难。
这是作者提供的解决方案的一部分:
def rotate_letter(letter, n):
"""Rotates a letter by n places. Does not change other chars.
letter: single-letter string
n: int
Returns: single-letter string
"""
if letter.isupper():
start = ord('A')
elif letter.islower():
start = ord('a')
else:
return letter
c = ord(letter) - start
i = (c + n) % 26 + start
return chr(i)
这里使用了取模运算,使得这个函数可以处理大写字母,但我搞不清楚为什么!很明显,使用它可以让我们在大写字母的ASCII值到达末尾后重新开始,但我还是不明白这个机制是怎么运作的。
2 个回答
2
模26其实和字母的大小写没有直接关系,它的作用是让字母序列在到达末尾后能重新回到开头。
举个简单的例子,假设我们做一个“旋转1”的操作:把字母表的字母当成1到26的数字,然后加1。如果输入是'a',那么1+1=2,结果就是'b';如果输入是'z',那么26+1=27,但字母表里没有第27个字母!所以我们要计算27模26,结果是1,这样就“旋转”回到'a'了。
在上面的实现中,处理大小写的关键在于start
的定义,它把ASCII码的位置转换成1到26的数字,然后再进行旋转,最后用同样的偏移量把结果转换回来。
2
试着把这个过程分成几个步骤,然后打印出中间的数字。或者,更好的是,可以在一个在线可视化工具中运行它。
比如说,用字母'Q'
和数字13,你最终会得到:
'Q'.isupper() is true
start = ord('A') = 65
c = ord('Q') - start = 81 - 65 = 16
i = (c + n) % 26 + start = (16 + 13) % 26 + 65 = 29 % 26 + 65 = 3 + 65 = 68
chr(i) is 'D'
你可以看到,关键的部分是(16 + 13) % 26
。所以我们来试着对从0(代表A
)到25(代表Z
)的每一个数字运行这个公式,看看会发生什么:
>>> for i in range(26):
... print ((i + 13) % 26),
13 14 15 16 17 18 19 20 21 22 23 24 25 0 1 2 3 4 5 6 7 8 9 10 11 12
加上数字后再对26取余,意味着当你到达26时,就会回到0。就像在时钟上,把23:00加1小时会变成00:00(或者,如果你是美国人,把12:00加1小时会变成1:00)。