我一直在阅读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值的开头重新启动,但我不明白它背后的机制。
尝试将其分解为步骤,并打印出中间的数字。或者,更好的是,在一个在线可视化工具中运行它。
例如,字母'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)。
模数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,然后使用相同的偏移量将结果转换回来。