Saturday 4 April 2015

Throwback Sunday: Looking back at "Adapting in_between() for Binary Search Trees"

Alex makes a good point about art and programming in her SLOG. I couldn't agree more with her. Programming can be a tricky mistress sometimes, yet always creative and fun. This last semester I have had some experience helping my wife with her Art & Code projects and, though Java is not my area of expertise, I did learn a lot about what programming can do for the arts. Moreover, being creative can lead you to new ways to solve old problems. Hence, we will look back at one of the problems seen previously, Adapting in_between() for Binary Search Trees, and see how new ideas on recursiveness could shape a new answer to this question, namely Easy Recursion.

Since adapting some code by adding new lines is hardly groundbreaking, let's come back with a new solution involving our triad of One Function, One Helper and One Global Variable. The question was how to look for values inside a BST that satisfies a group of parameters (ie range of numbers, multiple of 3). This can be done rather easily with the triad. Take a look at the code below:

_gather_values = []

def in_between(node, start, end):

    _gather_values.clear()
    
    _get_values(node, start, end)
    
    return _gather_values


def _get_values(node, start, end):
    
    if node.data > start and node.data < end and node.data % 3 == 0:
        _gather_values.append(node.data)
        
    if len(node.children) != 0:
        for x in node.left:
            _get_values(x, start, end)
        for y in node.right:
            _get_values(y, start, end)
    
Now, it does have a major flaw: it does not take advantage, nor tries to take advantage of, the properties of BSTs. The code at the end of the helper function could be changed to cover the properties of BSTs (left branch goes down, right branch goes up in value). Regardless, this could be improved quickly. The point was to show that this code, which solves the problem, could be constructed easily.

Looking back at that old post, I can see how many different ways you can solve a problem. Creatively, or not, it is always exciting. Whilst I aim for writing elegant code, this does not negate the power of quick, easy solutions. Let us keep coming with creative solutions, and improve upon our code writing.

Holy Mutations! Things getting dicey from now on...

We have done a lot work traversing trees and linking nodes. While it has been all fun, we now need to step things up a notch: we need to mutate the contents inside these trees and linking nodes. Let us remember that the data in each node is mutatable, and thus can be changed at any spot in time. Let us observe at one of the examples of mutation, in this case, of binary search trees.

Delete in Binary Search Trees
Algorithm
(Note: Remember that in a BST, the left child is smaller than the node, and the right child is bigger than the node)


  1. If the node is not a node, then there is nothing to do
  2. If data is less than the data in the node, go down the left (which has smaller values)
  3. If data is more than the data in the node, go down the right (which has bigger values)
  4. If node has fewer than two children, then return the existing one (which essentially brings down the only child if the deleted node has only got one child)
  5. Otherwise, if both children exist, replace the data with the data in the largest child in the left subtree and recursively work on the data from the left node, and finally return that node

On this final step, it is important to notice that, when deleting an element from a BST, you need to pull the item from the left child since the right child is always bigger than the node. To make this clear, we could follow this example:

            8
        6
    5
            4
        3
2
    1

Let's delete the five (5). Then we need to to grab a value to replace the five, and it only makes sense to take the largest child from the left. In this case it would be the four (4). The four is still smaller than the rest of the right children, and bigger than the left children. Then we would get the following:

            8
        6
    4
        3
2
    1

Code
This code was taken from class, so credit to whoever wrote it! It is shown here to give more context to what was done above.

def delete(node, data):
    ''' (BTNode, data) -> BTNode

    Delete, if it exists, node with data and return resulting tree.

    >>> b = BTNode(8)
    >>> b = insert(b, 4)
    >>> b = insert(b, 2)
    >>> b = insert(b, 6)
    >>> b = insert(b, 12)
    >>> b = insert(b, 14)
    >>> b = insert(b, 10)
    >>> b = delete(b, 12)
    >>> print(b)
            14
        10
    8
            6
        4
            2
    <BLANKLINE>
    '''
    return_node = node
    if not node: # Step 1 from the algorithm
        pass
    elif data < node.data: # Step 2 from the algorithm
        node.left = delete(node.left, data)
    elif data > node.data: # Step 3 from the algorithm
        node.right = delete(node.right, data)
    elif not node.left: # Step 4 from the algorithm
        return_node = node.right
    elif not node.right: # Step 4, again, from the algorithm
        return_node = node.left
    else: # Step 5 from the algorithm
        node.data = _find_max(node.left).data
        node.left = delete(node.left, node.data)
    return return_node

There is a helper function that traverses the right children for the highest value. It is used on the function above so that the highest value from the left child branch is brought and used as the new data of the node, and everything is brought down the left children. After all, the highest value from the left child will be found on the left child's right branch.

def _find_max(node):
    ''' (BSTNode) -> BSTNode
    
    Return maximal node in the BST rooted at node.
    Assume node is not None.
    
    >>> b = BTNode(8)
    >>> b = insert(b, 4)
    >>> b = insert(b, 2)
    >>> b = insert(b, 6)
    >>> b = insert(b, 12)
    >>> b = insert(b, 14)
    >>> b = insert(b, 10)
    >>> _find_max(b).data
    14
    >>> _find_max(b.left).data
    6
    '''
    return _find_max(node.right) if node.right else node

The Challenge
I wish I could say this could is easy to understand. Once you analyze it, it makes sense how it works and what it does. Regardless, there is a difference between knowing what to do and knowing how it needs to be done. The algorithm is not that intuitive, and requires close attention to notice exactly why things happen, and then how.


Friday 3 April 2015

Easy Recursion: One Function, One Helper, One Global Variable

Just like Andrew said in his SLOG, when working on recursive code, it is often the case that simple instructions will not solve a problem. But what if I were to tell you that there is a simple way to achieve recursion? You would probably tell me I am lying, or otherwise say that I have probably broken Python (just like Dima told me when I mentioned I found a way). So what is the solution I found? It can be broken down into these elements:

  • One Global Variable: Holds the information gathered by recursive code
  • One Helper Function: Uses recursion to gather information into the global variable
  • One Function: Clears the global variable, executes the helper function, and returns the global variable
Of course I would be penalized for efficiency, but bear in mind that this is an easy solution to what can seem a murky task to many. It uses recursion, yes, and it solves a problem, yes. Later on when you have more experience programming you can ditch this, but as a temporary solution, it should work great.

Let's put things into perspective. This is what we have seen in class:

def list_all(t):
    ''' (Tree) -> list

    Return list of values in t.

    >>> t = Tree(0)
    >>> list_all(t)
    [0]
    >>> t = descendents_from_list(Tree(0), [1, 2, 3, 4, 5, 6, 7], 3)
    >>> L = list_all(t)
    >>> L.sort()
    >>> L
    [0, 1, 2, 3, 4, 5, 6, 7]
    '''
    # implicit base case when len(t.children) == 0
    return [t.value] + gather_lists([list_all(c) for c in t.children])

(Credit to whoever did this code from Lab 05 solution)

This code is elegant, short, beautiful. It uses a helper function gather_lists(), which concatenates all sublists, and that is about it. The return line is only one line long. Now this will look like a travesty: I will do exactly the same, but using the method described before.

# Global variable here will hold all the info gathered
_all_elements = []

def list_all(t):
    ''' (Tree) -> list

    Return list of values in t.

    >>> t = Tree(0)
    >>> list_all(t)
    [0]
    >>> t = descendents_from_list(Tree(0), [1, 2, 3, 4, 5, 6, 7], 3)
    >>> L = list_all(t)
    >>> L.sort()
    >>> L
    [0, 1, 2, 3, 4, 5, 6, 7]
    '''

    # Clear the global variable first
    _all_elements.clear()

    # Call helper function
    _get_elements(t)
    
    # Return global variable
    return(_all_elements)
    
def _get_elements(t):
    ''' (Tree) -> None
    
    Gather all objects from the tree rooted at t in the global
    variable _get_elements
    '''
    
    # Append value to global variable
    _all_elements.append(t.value)
    
    # If there are children...
    if len(t.children) != 0:
        # Recursively work with the children
        for x in t.children:
            _get_elements(x)

So it does the same, but it is easier to understand, and easier to construct. It is also longer, less elegant, and probably not very efficient. It might be vulnerable to outside interference since it relies on using a global variables as a temporary vehicle.

Challenges
Recursion can be very challenging, but having a way to split the steps to visualize it better, while achieving results, can be encouraging. I encourage everyone to apply this method when unsure about what to do.

The challenge, hence, is how to get from this method to the more elegant, efficient one. And also the question on whether it consumes much more computation power than the elegant method. Perhaps it is not way better, but only slightly better.

Sunday 15 March 2015

Linking Nodes: Wrapping up your one child tree

The instant I read about Linked Lists, what I thought about was the impossible bet explained in Minute Physics. The solution to that puzzle involved a linked list of the sorts and, while the video is not about linked lists, it does outline one of the benefits of this data structure: it is an organized way to sequentially traverse through information.

What is a Linked List Node (LLNode)?
It is an object that has two pieces of information:

  • value = object held by the node (e.g. a number)
  • next = reference to the next node (i.e. another LLNode)

Does this ring any bells? Well, of course: it is identical to the attributes from our original Tree, with next instead of children (and next being the next node, while children was a list of nodes).

What is a wrapper?
Nodes could go on forever, or perhaps not. When we want to use a particular section of a chain, we use a wrapper to define the beginning and the end of it, as well as the size. The attributes of a wrapper are the following:

  • size = size of chain
  • start = starting node
  • end = ending node
As the name implies, the wrapper wraps itself around the nodes.

The Challenge

The appearance of a wrapper changes everything! While we used to use recursion in a kind of happy-go-lucky way, we now need to keep in mind that recursive code has to deal with the nodes inside the wrapper. Hence, it will have a different nature since changes will need to be made on the nodes and/or the wrapper. These are some examples and what they change:

  • Delete Node: Nodes + Wrapper
  • Change Node Value: Nodes
  • Add Node: Nodes + Wrapper
  • Shorten Wrapper: Wrapper
Let's keep these in mind for the next post, where we will explore how recursive code can change the wrapper and the nodes.

Adapting in_between() for Binary Search Trees


Last week we had the second Term Test, and one of the questions we had involved manipulating a piece of code we had seen in the lab. While I am usually horrible writing recursive code, I have a good time interpreting it. Therefore, today we are going to adapt one of the functions seen in class: list_between(). We have changed the function a little bit and named it in_between().

This is the code we are working with:
def in_between(node, start, end): ''' (BTNode, object, object) -> list

 Return a Python list of all values in the binary search tree
 rooted at node that are between start and end (inclusive).

 >>> in_between(None, 4, 11)
 []
 >>> b = BTNode(8)
 >>> b = insert(b, 6)
 >>> b = insert(b, 2)
 >>> b = insert(b, 5)
 >>> b = insert(b, 11)
 >>> b = insert(b, 7)
 >>> b = insert(b, 1)
 >>> in_between(b, 4, 5)
 [5]
 >>> L = in_between(b, 6, 11)
 >>> L.sort()
 >>> L
 [6, 7, 8, 11]
 '''

 #when there is no node
 #return an empty list
 #alternatively use: 
 #if node is None:
 if not node:
  return []

 #you need to act recursively,
 #gathering numbers from node.data
 #while checking if you can go
 #further left of further right
 else:
  #go further left
  lst_left = (in_between(node.left, start, end) 
    if node.data > start 
    else [])

  #go further left
  lst_right = (in_between(node.right, start, end) 
    if node.data < end 
    else [])

  #check on the node  lst_node = ([node.data] 
    if (start <= node.data <= end) 
    else [])

 #join all lists

 return lst_left + lst_node + lst_right


Let's say for example you want to do something a bit different. Now you want to search strictly for numbers that are multiples of three. This can be accomplished with a simple addition. What we are going to do is add the condition node.data % 3 == 0 in the part of the code where the objects are checked for compliance with start and end. Additionally, we are going to change the docstring so that it is strictly for numbers.

This is the modified code:
def threes_in_between(node, start, end):
 ''' (BTNode, number, number) -> list

 Return a Python list of all values in the binary search tree
 rooted at node that are between start and end (inclusive),
        and are multiples of three.

 >>> in_between(None, 4, 11)
 []
 >>> b = BTNode(9)
 >>> b = insert(b, 6)
 >>> b = insert(b, 2)
 >>> b = insert(b, 5)
 >>> b = insert(b, 11)
 >>> b = insert(b, 7)
 >>> b = insert(b, 1)
 >>> in_between(b, 4, 5)
 [5]
 >>> L = in_between(b, 6, 11)
 >>> L.sort()
 >>> L
 [6, 9]
 '''
 #when there is no node
 #return an empty list
 #alternatively use: 
 #if node is None: 
 if not node:
  return []

 #you need to act recursively,
 #gathering numbers from node.data
 #while checking if you can go
 #further left of further right else:
  #go further left
  lst_left = (threes_in_between(node.left, start, end) 
    if node.data > start 
    else [])

  #go further left
  lst_right = (threes_in_between(node.right, start, end) 
    if node.data < end 
    else [])

  #check on the node
  lst_node = ([node.data] 
    if (start <= node.data <= end 
     and node.data % 3 == 0) 
    else [])

 #join all lists
 return lst_left + lst_node + lst_right

So it was a simple change that allowed us to be selective about the data we gather. It is important to notice, too, that lst_node is the only list that gathers data, while the other ones just act recursively on the left and right, deciding where to stop (remember that a Binary Search Tree's data increases to the right and decreases to the left).

Challenges
You might feel inclined to add the condition to lst_right and lst_left, too. 
 else:
  #go further left
  lst_left = (threes_in_between(node.left, start, end) 
    if node.data > start and node.data % 3 == 0
    else [])

  #go further left
  lst_right = (threes_in_between(node.right, start, end) 
    if node.data < end and node.data % 3 == 0
    else [])

However, this would be a huge mistake since their only job is to dig (or climb, since it is a tree) further down the tree with start and end as limits, finally gathering the numbers when lst_node does its magic.


Sunday 8 March 2015

Doodad Directed Programming (An overview on OOP)

I feel humbled by my friend Dima's explanation of Object Oriented Programming (OOP). It is a very entertaining account that includes both technical terms and his personal experience on learning about and applying this concept. Having said that, I will do my best to explain what OOP is and why it is important.

What is Object Oriented Programming (OOP)?
Some friends of mine believe that programming involves a lot of binary digits and their bending Ã  la Matrix. What seems to be a lesser known fact about programming is that there are different levels of communication between programmers and machines.

Diagram 1: Representation of programmer / machine levels of communication

As you can see in Diagram 1, we have hardware at the bottom and programming languages at the top. Machine language is the only language that machines understand and it is composed mainly of numbers, and each CPU uses a different language, hence it is impractical for a person to learn it. To make it practical for people to create programs, high-level languages were created. These resemble English (i.e. if you don't know English, you might have a harder time understanding why things are the way they are in programming), but some more than others.

OK, this explanation seems to have drifted away from OOP. Do I still have a point to make here? Yes, and it has to do with the mentality applied when creating high-level languages. Machines do not know what a number is, or the heavy burden of daily life. They know about numbers (probably in the form of binary gates XOR XOR 110011101) and that's it. Hence, when creating a language they needed to tell the machine what a number is and how it works. They also needed to tell the machine what a string is and how it works. And so on...

But why should the person who engineers a programming language carry the burden of describing everything in the whole wide world. We find objects in the world, and they have definitions and rules. OOP is centered around creating objects, properly defined through attributes and following rules through methods, and them being instrumental when fulfilling a goal.

In short, OOP revolves around creating and using objects (called classes in Python and other programming languages), which are data structures that have attributes and methods.

What is a class?
A class is a data structure that has both attributes and methods (you are probably tired of listening these two words). An attribute is any form of data that is contained inside the class. A method is a function that is part of the class, and typically focuses on working with the attributes that are part of the class.

Classes can have children. Why couldn't they? As long as they are caring parents and pass on some attributes and methods, there should not be a problem.

What is inheritance?
Inheritance occurs when a subclass is initiated. This subclass will have all the attributes and methods of the parent class, unless they are overridden in the subclass itself. The idea is that you can make a main, generic class and have its offspring be specific for a certain task.

Check my POST on inheritance for a more detailed explanation with examples!

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.