EX03 - If and While


Introduction

In this exercise we will build on our Caesar Cipher from EX02 to handle uppercase and lowercase strs of any length.

Before beginning, make sure that you have your EX02 working correctly and you have completed. Additionally, be sure you have completed Lessons 10, 11, and 12 before beginning this exercise.

Setup Exercise Directory

Open the course workspace in VS Code (File > Open Recent > comp110-workspace) and open the File Explorer pane. Expand exercises.

Right click on the word exercises and select “New Folder”.

Name the folder exactly: ex03 and create a new file called cipher.py. Copy the contents of EX02’s cipher.py into this file as a starting point.

Part 1 - Handling any alphabetic character

We want our new and improved Caesar Cipher to be able to handle both uppercase and lowercase letters. Lucky for us, python has several built-in str methods that can help us check the case of character.

isupper and islower

Python has a built-in str method, isupper, that returns True if every alphabetic character in the str is uppercase. Since we are only dealing with single-length strs in encode_char and decode_char this method will definitively tell us if we are looking at a capitalized letter or not. You can try the following code out in a REPL

>>> "c".isupper()
False
>>> "D".isupper()
True

Unsuprisingly, there is also an islower method that does just the opposite.

>>> "c".islower()
True
>>> "D".islower()
False

Refactoring encode_char and decode_char

With the built-ins methods shown above and our knowledge of if statements, we have the tools under our belt to update our _char functions to handle any character A-Z or a-z. The only difference between encoding an uppercase vs. lowercase character is the int value used to normalize our single-length str. In EX02, we used 97 since that is the ASCII code for a. Now, we still want to use 97 for lowercase letters, but in the case of an uppercase character we want to use 65. This might seem random but if we look up A here: https://en.wikipedia.org/wiki/ASCII#Printable_characters we see that its code is 65.

Functional Requirements – encode_char

  1. Have a function with the following signature:
    1. Name: encode_char
    2. Arguments: a str that can be assumed to be single-length
    3. Returns: a single-length str
  2. The new str should be the parameter given shifted one letter to the right in the alphabet, per the mapping shown above.
  3. Your function should make use of the ord and chr built-in functions.
  4. You can assume only letter characters will be tested on your function. You cannot assume whether the character will be upper or lowercase, though, so your function should handle both cases appropriately.

Functional Requirements – decode_char

  1. Have a function with the following signature:
    1. Name: decode_char
    2. Arguments: a str that can be assumed to be single-length
    3. Returns: a single-length str
  2. The new str should be the parameter given shifted one letter to the left in the alphabet, per the mapping shown above.
  3. Your function should make use of the ord and chr built-in functions.
  4. Ensure your function has the expected behavior for both uppercase and lowercase characters.

Part 2 - Handling a variable length str

The last step is to update our encode_str and decode_str functions to handle strs of any length. After this, your Caesar Cipher will be powerful enough to handle any word you give it!

To process a sequence using a loop, consider that the subscription notation expects an integer expression within the square brackets. For example, the following snippet prints every character of a str on its own line:

letters: str = "abcdefghijklmnopqrstuvwxyz"
i: int = 0
while i < len(letters):
   letter: str = letters[i]
   print(letter)
   i = i + 1

Notice using the len function to determine the length of any string allows this loop to process the letters str no matter its contents. You will need to make use of a similar strategy for encoding and decoding a str.

Functional Requirements – encode_str

  1. Have another function with the following signature:
    1. Name: encode_str
    2. Arguments: a str of any length.
    3. Returns: a str with the same length as the argument.
  2. The result of this function should be the parameter str with each letter shifted one to the right.
  3. Call encode_char inside this function for each letter of the parameter.
  4. Use a while loop with a counter variable to complete this function. Refer to lesson 11 for information on while loops.

Functional Requirements – decode_str

  1. Have another function with the following signature:
    1. Name: decode_str
    2. Arguments: a str of any length.
    3. Returns: a str with the same length as the argument.
  2. The result of this function should be the parameter str with each letter shifted one to the left.
  3. Call decode_char inside this function for each letter of the parameter.
  4. Use a while loop with a counter variable to complete this function.

Testing your program

To check that your functions work as expected, you can load your cipher file into a REPL by opening up a new REPL, and then running from exercises.ex03.cipher import encode_char, encode_str, decode_char, decode_str. From here you can practice calling your functions and seeing if the results match what you expected. Once you have completed parts 1 and 2, you should be able to try combining function calls to decode and encode.

For example, decode_char and encode_char are inverses of each other. This means if you apply encode_char to something followed by decode_char, you should end up with what you started with.

decode_char(encode_char("a")) --> returns "a"
decode_char(encode_char("A")) --> returns "A"

This same property holds for decode_str and encode_str. You should see the following behavior:

decode_str(encode_str("flex")) --> returns "flex"
decode_str(encode_str("CaEsArCiPheR")) --> returns "CaEsArCiPheR"

Style and Documentation Requirements

For the both parts of the exercise, we will manually grade your code and are looking for good choices of meaningful variable names. Your variable names should be descriptive of their purposes. We will also manually grade to check that you declared your variables with explicit types.

Once your program is working, add a docstring at the top of your file with a one-sentence description of your program’s purpose.

Then, add your __author__ variable assigned to your name and e-mail address.

Lastly, there should be no magic numbers in your code. Make any magic numbers into named constants. Hint: due to our changes in refactoring encode_char and decode_char, we should have introduced another magic number into our code.