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!