The exercises on this worksheet concern our discussion of object-oriented programming (from lectures 4, 5, and 6).
Complete these coding exercises. Each one asks you to write one or more programs or modules. Even though these are not collected, you should get into the habit of following the coding standards that apply to all graded work in MCS 275.
The Kelvin temperature scale is one where the temperature of 0K (0 Kelvins) represents absolute zero, the lowest possible temperature. One Kelvin is the same size as one degree celsius, 0K is about -273.15°C.
It doesn't really make sense to add two temperatures; doing so would be a bit like adding one student's score on a quiz to another student's score on that quiz. You could come up with a scenario where that's the right thing to do, but in most cases, the answer would not have any direct meaning.
On the other hand, the ratio of two temperatures is meaningful, and has no units. 100K divided by 50K is equal to 2, which represents the ratio of average kinetic energies of the particles of an object. Similarly, it does make sense to multiply temperatures by numbers; for example, multiplying a temperature by 2 answers the question "If I doubled the average kinetic energy of each particle in this object, what would its new temperature be?".
(One reason for using Kelvins here is that multiplication of Kelvins is meaningful, whereas multiplication of Celsius or Fahrenheit degrees is not.)
Use operator overloading to make two classes:
class Temperature
to represent a temperature measured in Kelvins, given as a nonnegative float
Temperature
object in the REPL or with print()
should show something like 301.5K
Temperature
objects gives a TemperatureDifference
objectTemperature
by an int or float gives another Temperature
Temperature
object by another Temperature
object gives a floatTemperature
with the same number of Kelvins.class TemperatureDifference
to represent the difference between two Temperature
objects
TemperatureDifference
object in the REPL or with print()
should show something like 301.5K difference
TemperatureDifference
objects gives another TemperatureDifference
objectTemperatureDifference
object and a Temperature
object (in any order) gives another Temperature
object.TemperatureDifference
with the same number of Kelvins.TemperatureDifference
by another gives a floatTemperatureDifference
by an int or float gives another TemperatureDifference
The test code below should work when you're done, assuming you've imported these two classes into the global scope. The comments show the expected output.
# Test code assuming `Temperature` and `TemperatureDifference` exist in global scope
T0 = Temperature(273.15) # 0°C
T1 = Temperature(373.15) # 100°C = boiling point of water at 1 atmosphere pressure
print(T0) # 273.15K
DeltaOneK = TemperatureDifference(1) # 1K difference
print(T1-T0) # 100K difference
print((T1-T0)/DeltaOneK) # 100
print(T0==T1) # False
print(T0==Temperature(273.15)) # True
print(2*(T1-T0)) # 200K difference
print((T1-T0)*2) # 200K difference
print(T1/T0) # 1.3660992128866922
T2 = 2*T0
print(T2) # 546.3K
T2 = T0*2 # try multiplying in other order
print(T2) # 546.3K
print("""If a sample of helium starts at 0°C, you'd need to heat it to a temperature
of {}°C in order to double the average kinetic energy of the atoms.""".format(
(T2-T0)/(DeltaOneK)
)) # Should report need to heat to 273.15°C
This is another exercise about the robot simulations; you should work on it eventually, but if you are tired of that specific example and want to start with subclassing applied to another topic, go on to question 3 and come back to this one.
Any time after 3pm on Monday January 25, download the files for the robot simulation developed in Lectures 5-6:
Now, take the MarchBot
class you made in worksheet 2 and convert it to a subclass of Bot
in bots.py
, so that it uses Point
and Vector
classes instead of storing pairs of integers and takes advantage of the methods inherited from Bot
.
Then, add two new robots to bots.py
that are subclasses of Bot
:
DelayMarchBot()
Vector(1,0)
, but any direction can be specified in the constructor)PauseMarchBot()
MarchBot
, because they don't exhibit the behavior of MarchBot
, which steps in the same direction every time.)Add these robots to the simulation and confirm they exhibit the expected behavior.
Build a module encoders
(in encoders.py
) containing classes for simple ciphers (or codes; ways of obscuring the contents of a string that can be undone later by the intended recipient).
There should be a base class BaseEncoder
that has two methods:
encode(self,text)
: Returns the string text
unchanged. Subclasses will alter this behavior.decode(self,text)
: Returns the string text
unchanged. Subclasses will alter this behavior.It should be the case that
obj.decode(obj.encode(s)) == s
is true for any string s
, and for any object obj
that is an instance of BaseEncoder
or subclass thereof.
Then, build subclasses of BaseEncoder
that implement encoding and decoding by different ciphers, including:
RotateEncoder
: Encoding rotates letters in the alphabet forward by a certain number of steps, e.g. so rotation by 5 turns "a" into "f" and "z" into "e" (because we wrap around when we reach the end of the alphabet). No transformation is applied to characters other than capital and lower case letters. Constructor accepts an integer, specifying the number of steps to rotate.
Rot13Encoder
: A subclass of RotateEncoder
that fixes the steps at 13, so that encoding and decoding are the same operation.
SubstitutionEncoder
: The constructor accepts two arguments, pre
and post
. The string pre
is a list of characters to be replaced when encoding, and string post
indicates the things to replace them with. For example, using pre="abcd"
and post="1j4e"
would mean that "a" is supposed to be replaced by "1", "b" by "j", "c" by "4", and so on.
pre="abc"
and post="bca"
should encode "banana" to "cbnbnb", and not "ccncnc".pre
and post
contain the same characters but in a different order. If that's not the case, then it would be impossible to ensure that decoding after encoding always gives the original text back again.You can find some test code below. The test code assumes all of the classes are in the global scope.
E = RotateEncoder(5)
s = E.encode("Hello world!") # Mjqqt btwqi!
print(s) # Mjqqt btwqi!
print(E.decode(s)) # Hello world!
F = SubstitutionEncoder("lmno","nolm")
s = F.encode("Hello everyone!")
print(s) # Hennm everymle!
print(F.decode(s)) # Hello everyone!
Work on these open-ended problems if you finish the exercises above. We don't plan to include solutions to these in the worksheet solutions, but we may do so if most people end up working on any of these.
The encoders in problem 3 don't handle the problem of communicating to your message recipient the information about what code you will use for future messages.
Add __str__
and __repr__
methods to the ciphers that give enough information so that a message recipient who is given encoded text and the return value of str(encoder_object)
would be able to instantiate an encoder and decode a message.
Design and implement another cipher as a subclass of BaseEncoder which isn't as simple as substituting letters with specified replacements. For example, you might make it so that the way a letter is handled depends on both the letter and the text that's been encoded so far. Confirm that your cipher
Make a robot class (a subclass of DestructBot
) that stands still for a specified number of steps and then self-destructs. But before it does so, this class calls a user-specified function. The function is given as an argument to the constructor. So, for example:
def bye():
"""Robot says goodbye"""
print("Thanks for including me in this simulation. I was glad to be written in Python. Goodbye.")
R = bots.DelayedActionBot(position=Point(3,3),lifetime=10,action=bye)
# ... code to run the simulation ...
should make a robot that sits at position (3,3) for 10 steps, prints a message, and then self-destructs.
The action
argument of the constructor should default to None
, and the class should know to not do anything if action==None
. That way, any code that works with DestructBot
will also work with DelayedActionBot
.