# Caesar ciphers ![centre-aligned Caesar wheel](caesarwheel1.gif) Letter-by-letter enciphering --- layout: true .indexlink[[Index](index.html)] --- # Enciphering and deciphering ## Arithmetic on letters Convert .plaintext[letter] → .plaintext[number] → .ciphertext[number] → .ciphertext[letter] Functions you will need ```python ord() chr() % ``` * What are good test cases? --- # The [string module](http://docs.python.org/3.3/library/string.html) is your friend ```python import string string.ascii_letters string.ascii_lowercase string.ascii_uppercase string.digits string.punctuation ``` --- # DRY and YAGNI Is your code DRY? --- # Doctest * Why document? * Why test? ```python def caesar_encipher_letter(letter, shift): """Encipher a letter, given a shift amount >>> caesar_encipher_letter('a', 1) 'b' """ if letter in string.ascii_letters: . . . ``` --- # The magic doctest incantation ```python if __name__ == "__main__": import doctest doctest.testmod() ``` --- # Doing all the letters ## Test-first developement 1. Write the tests. * They will fail. There is no code. 2. Write code until the tests pass. 3. Refactor. --- # Doing all the letters ## Abysmal ```python ciphertext = '' for i in range(len(plaintext)): ciphertext += caesar_encipher_letter(plaintext[i], key) ``` --- # Doing all the letters ## Bad ```python ciphertext = '' for p in plaintext: ciphertext += caesar_encipher_letter(p, key) ``` ...but easily generalisable --- # Doing all the letters ## Good (but unPythonic) ```python ciphertext = map(lambda p: caesar_encipher_letter(p, key), plaintext) ``` --- # Doing all the letters ## Best ```python ciphertext = [caesar_encipher_letter(p, key) for p in plaintext] ``` --- # Not all iterables are equal ```python ''.join() ```