Saturday, 7 February 2015

Recursion(Recursion(Recursion(...)))

Recursion is a powerful property of many programming languages. Alex's SLOG called it "code-ception", which I thought was a catchy name and a spot on way to describe it. Just like a dream inside a dream inside a dream, a recursive function is a function that goes inside itself, and then goes inside itself, and so on (as many times as needed). The difference between Inception and Recursion is that in Inception you go, while in a dream, into dreams of different people, while in Recursion you go, while the code is being executed, into the same code. Hence, the title Recursion(Recursion(Recursion(...))).

Why is it useful?
Save space; save trees. Well, not physical trees (unless for some reason you want to print your code [but not print() your code]), but memory space. The alternative is writing several functions with if statements that call each other when needed. A clear problem appears as you realize that you would have to write a lot of code if many steps are needed!

Tracing Back 
Trace back is a good exercise when wanting to understand what a piece of recursive code does. Let's try tracing back the following piece of code:

def looper(L): '''
 (list) -> int
 
 Awful docstring that explains nothing.
 '''

 return sum([looper(x) for x in L]) if isinstance(L, list) else 5

(Oh! BTW, that is a new trick I learned from the labs. You can make a return statement while implementing an if statement)

These are the examples that we will trace back:
looper([]) --> 0 #since L has no elements, recursion does not take place

looper([17]) --> sum([looper(x) for x in [17])
-- > sum([5])
--> 5

looper([1, 2, 3, 4, [5, 6]]) --> sum([looper(x) for x in [1, 2, 3, 4, [5, 6]])
--> sum([5, 5, 5, 5, 10]) #we do not solve when the function is executed inside
--> 30

looper(['some', 'here', 5, ['hello', 'there']]) --> sum([looper(x) for x in ['some', 'here', 5, ['hello', 'there']]])
--> sum([5, 5, 5, 10])
--> 25

Wednesday, 4 February 2015

Of Properties and property()

Cruising through Piazza I found someone asking a question about property(). While we have not seen it extensively in class (even being snuffed out of our lab), it is a very important function when wanting to exert more control on our object's attribute, thus limiting the control that a client might have over it. This is done for several reasons, one of them being security and another code consistency.

Examples:

  • For security: You have a variable that controls a game's score. You would not want someone to easily change the score by changing the value of the attribute!
  • For consistency: You have a method that involves, among other things, the division of two attributes. You would not want someone to change one of the attributes to 0 by mistake, crashing your program!

The person who posted the question was clearly unsure about how property() works, and the person who answered did not really expand much on its use, thus I decided to intervene and write some example code. Here is that example (with additional code):

class MagicNumber():
 '''
 A magic number with the attributes:
  x: int - The magic number
 '''
 
 def __init__(self):
  '''
  (MagicNumber) --> NoneType
  
  Initializes the magic number (self) with x as 42.
  '''
  #notice that we do not call it x, but rather _x
  #in order to keep it private
  self._x = 42

  def get_x(self):
  '''
  (MagicNumber) --> int
  
  Returns the magic number (x)
  '''
   #we simply return the value of x
   #or any thing we want
   #depends on what you want the client to see
   return self._x

  def set_x(self, num):
  '''
  (MagicNumber, int) --> NoneType
  
  If num is not zero, change the magic number (x) to num * 42.
  Otherwise, changes the value of the magic number to 42.
  '''
   #we can control how the attribute is modified
   #useful when we do not want certain values
   if num != 0:
    self._x = num * 42
   else:
    self._x = 42

  #important part!
  #we define here the properties of the attribute x
  #additionally, add a del_x and descriptor
  x = property(get_x, set_x)

#we do not address self._x, but rather self.x
>>> self.x = 1 #we just called the method set_x
>>> self.x #we just called the method get_x
42

Sunday, 1 February 2015

Like Father, like Son == Like Class, like Subclass.

For newcomers to programming, classes and subclasses are an amazing thing. Since part of the core learning in programming is using predefined classes (such as str, int, list, etc.), the ability to create your own classes is just incredible. Don't like how Python manages your strings? Bam! Create a subclass that inherits all of its properties, yet has different / additional functionality through code of your own. If you need a class that combines the power of the different predefined classes, you can always do it by having different objects inside your class.

I love a good analogy! Python delivers a good one with its class / subclass relationship. Just like father and son, in classes and subclasses the child can:
  • Inherit characteristics, in this case attributes and methods
  • Change some of the characteristics (and even create new ones!)
So even if your kid does not resemble you that much, he will still inherit some or all of your characteristics. You might have math in the list self.skills, yet he might have French instead of math. You might have a method called def snores() that is not implemented, yet he might have that method implemented. Even when your kid says "I want to be a Computer Scientist / Software Engineer / Programmer / etc., just like my dad", this will hint that your child might have the same purpose as you have. Bottom line, it is just a happy family of classes and subclasses.

From a programmers point of view, implementing classes in Python is a dead easy task. I'll come upfront and confess my love for Python: it is versatile, powerful, elegant, and so, so easy to use. Hence, let's start with a simple example:

You work for a small animal clinic. You are tasked with creating a combination of class / subclass that will be able to hold the information of each patient. The parent class defines an animal patient, while the subclasses define a specific type of animal (e.g. dog) patient.

**DISCLAIMER: THIS CODE WAS CREATED SPECIFICALLY FOR THIS SLOG. IT NEEDS MORE TESTING! IT WAS CREATED TO SHOW THE POWER OF CLASSES AND SUBCLASSES. YOU MAY USE SNIPPETS OF THIS CODE FREELY, BUT IF YOU WISH TO USE IT ALL, PLEASE LET ME KNOW! IF YOU HAVE ANY QUESTIONS ABOUT ANY STEP, FEEL FREE TO CONTACT ME. FINALLY, IF YOU USE THIS FOR AN ASSIGNMENT, THINK TWICE! IT CONSTITUTES ACADEMIC FRAUD.**

-----------------------------------------

#so that we can use the datetime type for recording dates
import datetime

#dict for switch-like method, inspired by
#Vincent's Slog!
#cool SLOG! go check it out!
dogshotlist = {1: "Booster Shot 1",
               2: "Booster Shot 2",
               3: "Booster Shot 3",
               4: "Rabies",
               5: "Bordatella"}

class AnimalPatient():
    
    def __init__(self, owner, name, breed, bdyear, bdmonth, bdday):
        """
        (AnimalPatient, str, str, str, int, int, int) -> NoneType
        
        """
        
        self.owner = owner
        self.name = name
        self.bday = datetime.date(bdyear, bdmonth, bdday)
        self.breed = breed
        self.shothistory = {}
        self.appointment = False
        self.appdate = datetime.date(2015, 1, 1)
        
    def createapp(self, appyear, appmonth, appday):
        """
        (AnimalPatient, int, int, int) -> NoneType
        
        """
        
        self.appdate = datetime.date(appyear, appmonth, appday)
        self.appointment = True

    def addshot(self, shotnum, shyear, shmonth, shday):
        """
        (AnimalPatient, int, int, int, int) -> NoneType

        Create the next sequential key in dict shothistory that holds
        the shot's name, determined by shotnum, and the date with shyear
        as year, shmonth as month, and shday as day
        
        Needs to be implemented in a subclass.
        
        """        
        #different animals have different shots, so we need a subclass
        #to specify what needs to be done, so we raise an error
        #if this is not used within a subclass with a working method
        
        raise NotImplementedError('Subclass needed')

    def missingshots(self):
        """
        (AnimalPatient) -> str
        
        Return string that displays the missing shots of the patient.
        
        Needs to be implemented in a subclass.

        """        
        #just like in add shot, we need something specific to an animal
        #subclass to execute this method
        raise NotImplementedError('Subclass needed')

    def __repr__(self):
        """
        (AnimalPatient) -> str

        Return string that represents how this object was initialized
        
        """
        
        #string representation of how the object was initialized
        return "AnimalPatient(owner = {}, name = {}, breed = {}, bdyear = {}, bdmonth = {}, bdday = {})".format(self.owner, self.name, self.breed, str(self.bday)[:4], str(self.bday)[5:7], str(self.bday)[8:10])
    
    def __str__(self):
        """
        (AnimalPatient) -> str

        Return string that displays the information held by this object
        
        """
        
        #not all animals have breeds! so you can make a different
        #__str__ method for those animals
        info = "Owner name: {}\nPet name: {}\nBirthday: {}\nBreed: {}".format(self.owner, self.name, self.bday, self.breed)

        shots = ""
        
        i = 1
        
        for shot in self.shothistory:

            shots += shot + ". " + self.shothistory[shot] + "\n"
        
        if self.appointment:
            appointment = "\nAppointment Date: {}".format(self.appdate)
        
        else:
            appointment = "\n**No Appointment Recorded**\n"
            
        return info + shots + appointment
    
    def __eq__(self, other):
        
        self.owner = other.owner
        self.name = other.name
        self.bday = other.bday
        self.shothistory = other.shothistory
        self.appointment = other.appointment
        self.appdate = other.appdate

    #we mention AnimalPatient here so that we can inherit its characteristics
class DogPatient(AnimalPatient):
    def __init__(self, owner, name, breed, bdyear, bdmonth, bdday):
        """
        (DogPatient, str, str, str, int, int, int) -> NoneType
        
        """        
        
        #initializes the parent AnimalPatient type to inherit
        #its methods and attributes
        AnimalPatient.__init__(self, owner, name, breed, bdyear, bdmonth, bdday)
        
        #dogs need anual deworming, hence we make an extra attribute here
        self.lastdeworm = datetime.date(2015, 1, 1)
        
    #this is a method exclusive of DogPatient
    def addlastdeworm(self, dwyear, dwmonth, dwday):
        """
        (DogPatient, int, int, int) -> NoneType
        
        Change lastdeworm date to dwyear for year, dwmonth for month, and 
        dwday for day
        
        """
        
        self.lastdeworm = datetime.date(dwyear, dwmonth, dwday)
    
    def addshot(self, shotnum, shyear, shmonth, shday):
        """
        (DogPatient, int, int, int, int) -> NoneType

        Create the next sequential key in dict shothistory that holds
        the shot's name, determined by shotnum, and the date with shyear
        as year, shmonth as month, and shday as day
        
        """   
        
        if len(self.shothistory) == 0:
            self.shothistory[1] = (dogshotlist[shotnum], datetime.date(shyear, shmonth, shday))
            
        else:
            self.shothistory[len(self.shothistory) + 1] = (dogshotlist[shotnum], datetime.date(shyear, shmonth, shday))
        
    def missingshots(self):
        """
        (AnimalPatient) -> str
        
        Return string that displays the missing shots of the patient.
        Needs to be implemented in a subclass.

        """           
        #creates shallow copy of the dogshotlist to avoid mutation
        missingshots = dict(dogshotlist)
        missingstring = "List of missing shots:\n"
        
        for i in self.shothistory:
            for j in missingshots:
                if self.shothistory[i][0] == missingshots[j]:
                    del missingshots[j]
                else:
                    pass

        if len(missingshots) == 0:
            missingstring = "\n**No missing shots**\n"
        
        else:
            k = 1        
            for l in missingshots:
                missingstring += str(k) + ". " + missingshots[l] + "\n"
                k += 1
            
        return missingstring
                
    #DogPatient has a new attribute, so we need a new eq method
    def __eq__(self, other):
        
        self.owner = other.owner
        self.name = other.name
        self.bday = other.bday
        self.shothistory = other.shothistory
        self.appointment = other.appointment
        self.appdate = other.appdate
        
        #this new attribute forces us to create a new __eq__
        self.lastdeworm = other.lastdeworm
        
        #alternatively, we could call
        #the method AnimalPatient.__eq__(self, other) and add the last line
        #for the extra attribute
    
    #we do need to change repr since the class has a different name
    #we do not make a str method since AnimalPatient has a pretty strong
    #and useful one
    def __repr__(self):
        """
        (DogPatient) -> str

        Return string that represents how this object was initialized
        
        """
        
        #same as for AnimalPatient, except that we need to change
        #AnimalPatient to DogPatient
        return "DogPatient(owner = {}, name = {}, breed = {}, bdyear = {}, bdmonth = {}, bdday = {})".format(self.owner, self.name, self.breed, str(self.bday)[:4], str(self.bday)[5:7], str(self.bday)[8:10])

-----------------------------------------

(I apologize for the poor formatting! I will check how to properly insert code into Blogger at some later stage. For now, you can copy and paste this code into Wing or other Python editor for better visualization!)

Let's test the code!

>>> d = DogPatient('Jose', 'Miguelito', 'Poodle', 2015, 9, 15)
>>> print(d)
Owner name: Jose
Pet name: Miguelito
Birthday: 2015-09-15
Breed: Poodle
**No Appointment Recorded**

>>> print(d.missingshots())
List of missing shots:
1. Booster Shot 1
2. Booster Shot 2
3. Booster Shot 3
4. Rabies
5. Bordatella

>>> d.createapp(2015, 4, 5)
>>> print(d)
Owner name: Jose
Pet name: Miguelito
Birthday: 2015-09-15
Breed: Poodle
Appointment Date: 2015-04-05

As you can see, the function createapp() was inherited from AnimalPatient, and missingshots() is specific to DogPatient.

Try taking this class / subclass combo for a spin ;)

Thursday, 22 January 2015

The Prose and The Python (aka Why geeks should write)

School is over and now you are in college! You dreaded all of those English classes and laughed about people going for their English majors. After all, you have a promising career in tech. Perhaps you will be your own boss and form your own start up; perhaps you will be found by a headhunter from an established company that will offer you an amazing starting salary. Whichever way you go, you find no room for writing proper documents in your life. Unfortunately, by doing that you are handicapping yourself in several ways! Office environments thrive when there is proper communication, both verbal and written, between the different elements that make the company. Furthermore, writing properly will also help with your programming skills. Hence, we writing helps us in two dimensions: inner and outer.

Starting with the outer dimension, proper communication is vital wherever you go and whatever you end up doing. If you wish for people to do as you want, you need to write properly and concisely. Being vague or confusing will get you in a lot of trouble! Imagine that you want a specific method to be created, but you were not clear about the arguments that would be involved, then the person working with you will probably spend ours elaborating something with the wrong arguments. When it is time to patch things up, more time needs to be spent. It could be worse if a lack of communication leads to calculations going wrong. For example, when NASA decided to send a probe to Mars in 1999, an asymmetrical use of units in its systems lead to the probe crashing. Some part of the system used imperial units, while the other used metric units.

However, communicating with other people is not the only useful aspect behind writing. When you write in prose, you are sharing information step by step. That is similar to how programming works, since the compiler does exactly that when reading your code. Hence, getting into the habit of writing logically sound sentences in prose could lead you to have a clearer mind when writing and reading code. Furthermore, when writing code you need to be precise about what you are trying to execute: there are no middle grounds! If you learn how to write concisely in English, how far would you be from writing concisely in code?

Finally, writing and expressing ideas properly also enhances your ability to read and understand ideas correctly. There is no better art critic than an artist himself since he has first hand knowledge about the techniques employed. If you are having a hard time understanding English instructions, then you should start writing yourself. You will see how, by improving the elements of your written words, you will be able to understand better the elements of someone else's written words.

So it is clear that everyone should write, specially people working with code! The effort you put into understanding higher level languages will be reflected once you move on to lower level languages. I guarantee that!

The Course Challenges
Besides the normal post secondary education challenges, this course brings some new challenges, completely different from the ones from CSC108. Since we already know the basics (AKA terminology and all the basic objects that we can play with), it is time to kick it up a notch with new classes, created by us, that will allow us to do some interesting things. I am looking forward to learning about the new data structures we will see, particularly linking nodes and binary search trees. I do not think they will be complicated to learn, yet there are two aspects surrounding them that might bring some trouble:

  1. Recursion
  2. Mutations
The problem with recursion is that it involves the function/method calling itself, carrying information forward from the last call to the first call. Sometimes it will branch out, and it seems challenging to understand how the data coming back will be used and organized.

On the other hand, mutations offer a different challenge. Making changes inside these data structures involve recursion of a higher level. Honestly, I haven't got a clue of how to do this!

This course will be interesting, challenging, and hopefully fun! Lots of attention to detail and algorithm building will be necessary.