1

我一直在阅读 Allen B. Downey 的“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 值的开头重新开始,但我无法弄清楚它背后的机制。

4

2 回答 2

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. A因此,让我们尝试在从 0(for )到 25(for )的每个数字上运行它,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(或者,如果你是美国人,将 1 小时添加到 12:00 即可到达 1:00)。

于 2014-08-09T00:21:57.837 回答
2

模 26 本身与大小写无关,它需要使序列回绕到开头。

考虑一个简单的“rot 1”:将字母表中的字母视为从 1 到 26 的数字,然后加 1。如果输入是 'a',则取 1+1=2 并得到 'b';如果输入是“z”,则取 26+1=27 - 但没有字母表中的第 27 个字母!所以你计算 27 mod 26 = 1,它“旋转”回“a”。

在上面的实现中,大写和小写的实际技巧是定义start,它在应用旋转之前将 ASCII 位置转换为数字 1 到 26,然后使用相同的偏移量将结果返回。

于 2014-08-09T00:30:00.490 回答