Friday, 28 February 2014

Week7 - LinkedList ADT

Hi Everyone!

This week we learned LinkedList ADT it reminds me of Stack ADT though it was generated from Tree ADT. I am not very good at it right now. We didn't finish this topic in lecture. Therefore, I will only talk about LinkedList's basic definition, its  __init__ method and its difference with Stack. At the end I will share with you my lab work for implementing Queue with LinkedList. I didn't finish the lab work during the lab time, so I spend extra hours to work on that. Any comment will be appreciated.  Hope I can say more in next week's post~

LinkedList Definition:

This kind of ADT is generated from Tree ADT, since we can think the LinkedList as a linear tree --- the tree only has one branch and every node has exactly one child. Therefore a LinkedList is a sequence of list each with a head (value) and a reference to rest (its successors).

LinkedList reminds me of Stack and Queue ADT that we learned from lecture and lab. They have very similar characteristic in the form of a linear "list". In addition, their methods such as __repr__, __eq__, is_empty are very similar. Even though some of these methods only appear in one ADT, I guess these methods can be implemented to other ADTs by the same logic. Therefore, I am not going to talked about them here. I will list the main difference between them as following and I will provide the corresponding functions after. 

__init__ method:

def __init__(self: 'LinkedList', head: object=None,
             rest: 'LinkedList'=None) -> None:
    """Create a new LinkedList"""
    self.empty = (head is None) and (rest is None)
    if not self.empty:
        self.head = head
        if rest is None:
            self.rest = LinkedList()
        else:
            self.rest = rest
I thought this __init__ method has no difference with those __init__ methods we implemented before. However, when I got to preprend method's "self.empty = False" I was confused. I thought the "self.item = item" has already showed that head is not None. But after I delete "self.empty = False" and rerun the code with the same example, I understood that only when self.empty = True, the code under this condition will run, self.item can be assigned with item. Otherwise, values cannot be assigned to variables and both self.head and self.rest are still None, as before.

Prepend Method with self.empty = False:
>>> lnk = LinkedList()
>>> lnk.prepend(7)
>>> lnk
LinkedList(7, LinkedList())

Prepend Method Code without self.empty = False:
>>> lnk = LinkedList()
>>> lnk.prepend(7)
>>> lnk
LinkedList()

LinkedList vs Stack: 

Stack: add item to the end, delete item from the end
LinkedList: add item to the front, delete item from the end

As for a LinkedList, the most difficult part I feel is their are linked. In Stack, we can add or delete item by simply attach or detach. In LinkedList, every time we change it, we need to consider both the head and the rest, modify the LinkedList as a whole.

Stack (push, pop):


def push(self: 'Stack', o: object) -> None:
        """Place o on top of the stack."""
        self._data.append(o)
def pop(self: 'Stack') -> object:
        """Remove and return the top item."""
        return self._data.pop()

LinkedList(prepend, decapitate):
def prepend(self: 'LinkedList', head: object) -> None:
    """Add new head to front of self """
    self.head, self.rest, self.empty = head, copy(self), False    
def decapitate(self: 'LinkedList') -> None:
    """Delete head from LinkedList self."""
    if self.empty:
        raise Exception("Can't decapitate empty list!")
    elif not self.rest.empty:
        self.head, self.rest = self.rest.head, self.rest.rest
    else: #self.rest.empty
        self.empty = True #if delete this line,  AttributeError: self has no attribute head
        del(self.head)
        del(self.rest)

*The logic for decapitate is first check if the LinkedList is empty, if it's empty raise error. If it's not empty, then we check if self.rest is empty. If self.rest is empty, the if we decapitate the LinkedList it will be empty. Therefore, we let self.empty = True, and then delete self.head and self.rest. If self.rest is not empty, then decapitate is equivalent to shift the LinkedList one unit to the left. Therefore, self.rest.head become self.head, and self.rest.rest = self.rest.

Implement Queue with LinkedList:

Queue ADT we've seen in our first lab, but at that time we implemented with reference to Stack ADT. In this week's lab, we implemented it with a LinkedList.

Queue: add item to the end, delete item from front

Queue(enqueue, dequeue) *implement by a linkedlist:

    def enqueue(self: 'Queue', item: object) -> None:
        """ Add item to the back"""
        if self.linked_list.empty:
            self.linked_list.prepend(item)
        else:
            self.back = self.back.rest
            self.back.prepend(item)
    def dequeue(self: 'Queue') -> None:
        """ Delete item from the front"""
        a = self.front()
        self.linked_list.decapitate()
        return a

We went to skiing during the reading week~ How's everyone's vacation?
I think the midterm is ok and I did well. How does everyone feel >< ?

Hope I will be more familiar with LinkedList next week~ See you there!

Saturday, 22 February 2014

Week6 - Binary Tree ADT

Hey Everyone,

This week we learned a new kind of ADT -- Tree. By learning this ADT, I am more and more familiar with creating a class to represent a type. This Tree ADT leads to a new way of thinking problems. Like Stacks ADT, add to top & move from top; Tree ADT is also well-represented by its name. Tree has branches(subtree), leafs(node with no children) etc. A common kind of tree is a binary tree which contains at most two subtrees.




In this post, I am going to clarify several terminology that bothered me in past few weeks, then I will introduce a implicit recursion, finally I will talk about an important logic to write recursion function from binary Tree type object (simultaneously showing how to traversal a tree).


1. Module vs Class; Method vs Function

When we are implementing class Tree this week, we not only create class method but also outside class, in module functions.
Module is a collection of functions, variables grouped together n a file. Class  is another kind of object that is similar to module, it contains functions and variables. More important, a class is how Python represents a type.
method is another kind of function that is attached to a particular type. Therefore, only the functions inside a class can be called methods of that particular class.

In particular, __contains__(self: 'Tree', value: object) is method. arity(t: Tree) is a function.

Note another difference in type contract, in method 'Tree' has quotation mark with it whereas in function Tree has no quotation marks. In my opinion, this is because when a method is defined in a class, its first parameter must be variable that represents the object the method is being called on (by convention this called self). This quotation mark is used to separate this particular parameter with others.
Any comment will be appreciated :))


2. __repr__ method

This function shows how to use implicit recursion in a function, which means the exactly function name does not appear in the function but it also calls itself. This is very common when we create class method by modifying special methods.
def __repr__(self: 'Tree') -> str:
      """Return representation of Tree as a string"""
      return ('Tree(' + repr(self.value) + ', ' +
              repr(self.children) + ')')


3. Traversal a tree

We also learned how to "climb a tree" in different orders, that is what traversal a tree means. The most important thing I learned from this part is to use left tree and right tree to a separate a Tree and recurse a Tree. Instead of just recursing a method within Tree class, I guess this new logic is will be very useful in future studies.

There are three typical traversal: preorder, inorder and postorder.
Preorder: visit root first, then preorder left tree, finally preorder right
Inorder: visit inorder left tree first, then root, finally inorder right
Postorder: visit postorder left tree first, then right tree, finally root

I will provide the Preorder Traversal here since other two uses exactly same logic and their codes are very similar to this one.
def preorder(tl: 'TreeList') -> list:
    """
    Return list of values in tl in preorder
    >>> preorder([5, [4, None, None], [3, [2, None, None], [1, None, None]]])
    [5, 4, 3, 2, 1]
    """
    return [] if tl is None else ([tl[0]] + preorder(tl[1]) + preorder(tl[2]))

This is the first week we see the Tree ADT, I may add more thinking in future posts, if there is some~

We're going to have our 1st term test next week, GOOD LUCK TO EVERYONE!

Thursday, 13 February 2014

Week5 - More Recursion and Time Control

Hi Everyone,

This week we handed in our assignment1, which takes three of us a whole weekend to do. We found the recursive functions are the most difficult part. We reviewed many recursive functions from lectures and labs when we're writing our codes. There are three interesting codes which I think mostly improved my programming skills. In this week's post, I am going to share these three recursive functions with you. At the end of this post, I am going to talk about how to control program running time in Python. Thank you for reading :)

Binary Represent:

This is the function that uses recursion to build a program converting numbers into binary numbers, even more surprising, we can do for ternary numbers, quaternary numbers, and even k-nary numbers!

We develop our k-nary numbers by first solving the problem for binary numbers. Here we used the programming skill that we mentioned in Week3's post, solving a big programming problem by solving a simpler case first.

def bin_rep(n: int) -> str:
    """Return binary representation of n in base 2
    n --- non-negative integer
    >>> bin_rep(0) == "0"
    True
    >>> bin_rep(1) == "1"
    True
    >>> bin_rep(5) == "101"
    True
    """
    return bin_rep(n // 2) + bin_rep(n % 2) if n > 1 else str(n)
Note:
# The binary representation of n is the binary representation of
# n // 2 concatenated with the binary representation of n’s last bit
# provided n > 1, otherwise it’s just n as a string


def bin_rep(n: int, k: int) -> str:
    """Return binary representation of n in base 2
    n --- non-negative integer
    >>> bin_rep(0, 10) == "0"
    True
    >>> bin_rep(1, 2) == "1"
    True
    >>> bin_rep(5, 3) == "12"
    True
    """
    return bin_rep(n // k, k) + bin_rep(n % k, k) if n > k - 1 else str(n)
Note: 
# The binary representation of n is the binary representation of
# n // k concatenated with the binary representation of n’s last bit
# provided n > k - 1, otherwise it’s just n as a string

Nested Depth:

The second example is "nested_depth", which is a typical recursion-use function, and there's a small trick that we should be careful of: when we use a built-in method (such as 'max' in this example) applied to a list, we should make the list is not empty. The easy way to control a non-empty is by adding [0] (as highlighted in the code below).


def nested_depth(L):
    """
    Return if L is non-list, or 1 + maximum of depths of elements list L.
    Not defined for infinitely nested lists.

    >>> nested_depth(3)
    0
    >>> nested_depth([1, 2, 3])
    1
    >>> nested_depth([1, [2, 3], 4])
    2
    """
    return (1 + max([nested_depth(x) for x in L] +[0]) if isinstance(L,list) else 0)
 #Note: the [0] is in case we have an empty list

Tower of Hanoi (three stools, seq_of_move):
The last function I am going to talk about is from our Assignment. All the recursive functions we see so far only call themselves once; in this part of Hanoi function, we call the Hanoi function twice within itself. So we learned that the recursive function can happens several times. 

The following code is the sample code provided by Prof. Heap, I am not supposed to post our code here but we use the exactly same strategy.


def move_cheeses(n: int, source: int, intermediate: int, destination: int) -> None:
    """Print moves to get n cheeses from source to destination, possibly using intermediate"""
    if n > 1:
        move_cheeses(n - 1, source, destination, intermediate)
        move_cheeses(1, source, intermediate, destination)
        move_cheeses(n -1, intermediate, source, destination)
    else: # just one cheese --- no recursion required!
        print("Move top cheese from {} to {}".format(source, destination))


Control Program Running Time (with Time module):

We studied too hard to figure this out, even though it's simple haha. If someday you're asked to control your programming running time, there is a built-in model in Python called "time". The thinking here is set the init time first, then the second time, if the time difference is smaller than the one we want, let the program keep running, time2 continuously increase, until (time2 - time1) = time_difference, other codes will run after.

The sample code is as following:

time1 = time.time()
time2 = time.time()
while (time2 - time1) < time_difference:
      time2 = time.time()
#other codes

I am going to talk about scopes and tree class in next week's post. Come back soon~

Saturday, 1 February 2014

Week4 - Inheritance and Recursion

Hi Everyone! 

In this week's post I am going to talk about Inheritance and Recursion in Python.


Inheritance and Recursion are two very important process in computer programming. I was also confused when I first learn it. The example function tree_burst(), which I gave in Recursion part, is the one that I found most trouble with. Our prof taught it in class, but it's not very clear. I traced back every step of the recursion, wrote it out in my scrap paper and drew the turtle lines, but I am still confused with turtle_list.append(turtle.clone()).


Turtle module open a new world for me to realize that we can also use python to draw graphs, and this turns the scalar quantities to vector quantities. Furthermore, in this way, I guess we can also create any finite dimensional vector space using computer!
tree_burst() function is not a very good example for beginners to understand recursion, but I still decided to post it out because it is indeed a very impressive function. If anyone feel confused about it, please just skip and move to the next part. (If you have some good idea about the turtle_list.append(), please feel free to communicate! )

A. Inheritance


Literally, inheritance is the practice of passing on the feature to a child upon a parent. Well, in python, inheritance means the subclass(child) get most or all its method(feature) from its superclass (parent). 


There are three ways of inheritance:
1. Implicitly Inheritance
which means some or all the features that the child gets from his father didn't show off (we don't necessarily write it out in our subclass)
class Parent(object):

    def a(self):
        print "PARENT implicit()"

class Child(Parent):
    pass

*Here the method "a" is a implicit inheritance of the Child.

2. Override Explicitly
A child is not as same as their parent, same in the Python, sometimes a subclass behaves differently. We are going to override the function in the child, replacing the functionality inherited from its superclass. 
class Parent(object):

    def override(self):
        print "PARENT override()"

class Child(Parent):

    def override(self):
        print "CHILD override()"

3. Alter Before or After
A child can act completely different without their parent, but once their parent show up, they will behave like an angel. 
In python, we can add a parent version of the function runs among the child versions. 
class Parent(object):

    def altered(self):
        print "PARENT altered()"

class Child(Parent):

    def altered(self):
        print "CHILD, BEFORE PARENT altered()"
        Parent.altered(self)
        print "CHILD, AFTER PARENT altered()"

*These three inheritance we can use them separately or together
*Child(Parent): the Child class is a subclass of Parent class
*Parent(object), because every class is a subclass of the object class

B. Recursion


Recursion means "defining something in terms of itself", e.g. A cat is a kind of animal whose mother is a cat. In python, recursion means a function can call themselves to solve smaller subproblems. 
Note a finite recursive a function must have a base case, in order to indicate the function where to stop, otherwise the function will run forever and it will become an infinite recursive function.
def tree_burst(level, base, turtle):
    """Draw a symmetrical ternary tree of height level with initial edges
    of length base.
    """
    if not level: # Pythonese for level == 0
        pass
    else:
        turtle_list = []
        for h in range(3):
            turtle_list.append(turtle.clone())
            turtle_list[h].color(COLORS[h])
            turtle_list[h].setheading(120 * h)
            turtle_list[h].forward(base)
            tree_burst(level - 1, base/2, turtle_list[h])
* Note in this example, the italic format indicates the base case. Specifically, when level == 0, the recursion stops.

That's all for this week, thank you for reading! Next week I am going to talk about unittest, see you next week! :)