Today was the last day of classes. Danny gave us some topics to review for the exam and we took up a problem on one of the previous exams. The problem was to write a function which returns the head and tail of a doubly-linked list corresponding to an in-order traversal of a binary tree. I wasn't able to find the solution online, so I'll assume I shouldn't post it here. However, the solution was not so dissimilar in structure to this:
def inorder(node):
if not node:
return (None, None)
this = LLNode(node.data)
left_head, left_tail = inorder(node.left)
right_head, right_tail = inorder(node.right)
# do something (i.e. complete links between nodes)
return (left_head, right_tail)
For some reason, I thought that the solution would involve some sort of sandwiching structure. This is what I mean:
inorder()
# do something
inorder()
This is a typical inorder traversal algorithm, so I thought this was necessary to complete the function. Hence, the solution Danny gave seemed like a postorder traversal to me at first glance. After further inspection, I realized that the order of the recursive calls makes absolutely no difference. What does matter is in which order the nodes are linked. I verified this by coding up a preorder function. The function was almost identical in structure to the inorder function, but the node links were slightly different as I expected. It was nice to get this cleared up, since there was a very similar question in one of the labs which I remember I had completed, but I wasn't completely convinced with my solution.
Anyway, that's it for the course. It was great while it lasted.
Good luck on your exams!
Monday, 2 December 2013
Friday, 29 November 2013
Sorting Algorithms
Last week, we compared various sorting algorithms in our lab. Our results showed that Tim sort was the most efficient sorting algorithm, while quick sort and merge sort were the next fastest followed by insertion sort, selection sort, and bubble sort. For the purpose of this blog post, I will explain the general ideas of selection, quick and merge sort in order to offer an explanation as to why these efficiencies turned out to be the way they are.
Selection sort works by finding the minimum element from position i to the end of the list and then swapping this value with the value at index i. It takes about n - i operations to complete this step. Also, we require that i goes from 0 to n in order to completely sort the list. So the total number of operations will be given by (n + n - 1 + . . . + 2 + 1) = n*(n + 1)/2, which gives us a worst-case time complexity of O(n^2). Unfortunately the best-case time complexity is exactly the same as the worst-case time complexity for selection sort, since we require checking all n - i indices to ensure we have found the minimum value in the list (that means the running time will depend on n^2 even for a list that is already sorted).
Quick sort works by choosing some pivot (simpler implementations simply choose the first index of the list as the pivot), then partitioning the list into two sections: one section is a list containing values less than the pivot value while the other holds values greater than or equal to the pivot value. We sort these two sub lists recursively and then join everything together to get a sorted list. We perform about n operations for each recursive call, and we require about log(n) recursive calls to partition the list until only one element remains. So the average performance is O(n log n). There are cases, however, where this performance is much worse. As an example, consider a perfectly sorted list. Quick sort will choose the first index as the pivot. Since no element is smaller than the first element, one of the sub lists will be empty and the partition essentially does nothing. In this case, we require n recursive calls in order to split the list until one element remains (which means the worst-case time complexity is O(n^2)). A similar case occurs when the list is perfectly unsorted.
Merge sort works by splitting the list in half, sorting each half (recursively) and merging the sorted halves together. This has a similar time complexity as quick sort, we require log(n) recursive calls and perform n operations for each call so we have O(n log n). Unlike quick sort, there are no cases where merge sort behaves poorly. It is essentially guaranteed to run a time complexity of O(n log n).
Our data showed that quick sort runs faster on average. However, on sorted and unsorted data merge sort performed significantly faster (as expected). I would still prefer quick sort over merge sort since the average run time is faster. Also, to address the issues with sorted and unsorted data, we were taught a quick sort variant in Wednesday's lecture which chooses a random pivot. This variant does not have the same disadvantages on sorted data that the original algorithm has. And even though the worst-case complexity is still O(n^2), this is very rare. In fact, the chance that the minimum pivot would be chosen each time is 1/n! and similarly for the maximum pivot. So comparing merge sort and quick sort solely based on their worst-case time complexities is a meaningless comparison in this case. Therefore I claim that quick sort is generally faster than merge sort. Don't take my word for it though, this is simply my opinion based on what we have learned so far. I have noticed varying opinions on several forums about this topic. I am also aware that Tim sort is a hybrid sorting algorithm which uses merge sort. One would question why merge sort was chosen for this algorithm, so perhaps my opinion on this topic will change over time as I gain more knowledge.
Thanks for reading, let me know what you think about quick sort and merge sort in the comments.
Selection sort works by finding the minimum element from position i to the end of the list and then swapping this value with the value at index i. It takes about n - i operations to complete this step. Also, we require that i goes from 0 to n in order to completely sort the list. So the total number of operations will be given by (n + n - 1 + . . . + 2 + 1) = n*(n + 1)/2, which gives us a worst-case time complexity of O(n^2). Unfortunately the best-case time complexity is exactly the same as the worst-case time complexity for selection sort, since we require checking all n - i indices to ensure we have found the minimum value in the list (that means the running time will depend on n^2 even for a list that is already sorted).
Quick sort works by choosing some pivot (simpler implementations simply choose the first index of the list as the pivot), then partitioning the list into two sections: one section is a list containing values less than the pivot value while the other holds values greater than or equal to the pivot value. We sort these two sub lists recursively and then join everything together to get a sorted list. We perform about n operations for each recursive call, and we require about log(n) recursive calls to partition the list until only one element remains. So the average performance is O(n log n). There are cases, however, where this performance is much worse. As an example, consider a perfectly sorted list. Quick sort will choose the first index as the pivot. Since no element is smaller than the first element, one of the sub lists will be empty and the partition essentially does nothing. In this case, we require n recursive calls in order to split the list until one element remains (which means the worst-case time complexity is O(n^2)). A similar case occurs when the list is perfectly unsorted.
Merge sort works by splitting the list in half, sorting each half (recursively) and merging the sorted halves together. This has a similar time complexity as quick sort, we require log(n) recursive calls and perform n operations for each call so we have O(n log n). Unlike quick sort, there are no cases where merge sort behaves poorly. It is essentially guaranteed to run a time complexity of O(n log n).
Our data showed that quick sort runs faster on average. However, on sorted and unsorted data merge sort performed significantly faster (as expected). I would still prefer quick sort over merge sort since the average run time is faster. Also, to address the issues with sorted and unsorted data, we were taught a quick sort variant in Wednesday's lecture which chooses a random pivot. This variant does not have the same disadvantages on sorted data that the original algorithm has. And even though the worst-case complexity is still O(n^2), this is very rare. In fact, the chance that the minimum pivot would be chosen each time is 1/n! and similarly for the maximum pivot. So comparing merge sort and quick sort solely based on their worst-case time complexities is a meaningless comparison in this case. Therefore I claim that quick sort is generally faster than merge sort. Don't take my word for it though, this is simply my opinion based on what we have learned so far. I have noticed varying opinions on several forums about this topic. I am also aware that Tim sort is a hybrid sorting algorithm which uses merge sort. One would question why merge sort was chosen for this algorithm, so perhaps my opinion on this topic will change over time as I gain more knowledge.
Thanks for reading, let me know what you think about quick sort and merge sort in the comments.
Thursday, 28 November 2013
Memoization
In yesterday's class, Danny started the lecture by showing us a simple recursive algorithm which returns the nth Fibonacci number. It turns out that this algorithm was horribly inefficient since we were repeating several unnecessary calculations. So within one minute I came up with my own code for the Fibonacci function, here it is:
def fibonacci(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a # alternatively we can return b, if we want to start with 1 instead of 0
This is much quicker than the recursive implementation, it took me about 10 seconds to get the millionth Fibonacci number, while the recursive algorithm struggled with n = 40. During the entire lecture I was waiting for Danny to reproduce this code. Instead, he suggested something called memoization which stores information from previous calculations instead of recomputing them time and time again. So essentially we would store all Fibonacci numbers in some sort of data structure and before computing another number we would firstly check that it has not already been computed to save time. In my opinion, the Fibonacci sequence is not the best example of memoization seeing as the algorithm can be written easily without the need to store things in memory (again, look at the implementation I have presented). I do, however, see the potential advantages of memoization. It is a logical approach to solving a general problem of this form, sacrifice memory space to gain computation speed. Hopefully we'll be doing some more of this in the near future.
def fibonacci(n):
a, b = 0, 1
for i in range(n):
a, b = b, a + b
return a # alternatively we can return b, if we want to start with 1 instead of 0
This is much quicker than the recursive implementation, it took me about 10 seconds to get the millionth Fibonacci number, while the recursive algorithm struggled with n = 40. During the entire lecture I was waiting for Danny to reproduce this code. Instead, he suggested something called memoization which stores information from previous calculations instead of recomputing them time and time again. So essentially we would store all Fibonacci numbers in some sort of data structure and before computing another number we would firstly check that it has not already been computed to save time. In my opinion, the Fibonacci sequence is not the best example of memoization seeing as the algorithm can be written easily without the need to store things in memory (again, look at the implementation I have presented). I do, however, see the potential advantages of memoization. It is a logical approach to solving a general problem of this form, sacrifice memory space to gain computation speed. Hopefully we'll be doing some more of this in the near future.
Friday, 22 November 2013
Assignment Two and Test Two Results
In a previous blog post, I was discussing a test case error I had in my second assignment. Someone was kind enough to post some test cases for me to try out with the star node. I tried them out and passed all of them. When the results arrived on MarkUs, I was finally able to see the test suite and find exactly where I went wrong. Here was my code for the star node:
if r.symbol == '*':
return s == '' or any(root_match(r.children[0], s[:i]) and
root_match(r, s[i:]) for i in range(len(s) + 1))
The code failed on regex_match(RegexTree('e*'), '0'), it produces an infinite loop in this case. The key here is the splicing index. My method calls itself with the same string and same regextree, which indeed produces a stack overflow of recursive calls. After investigating, I found the correct code to be:
if r.symbol == '*':
return s == '' or any(root_match(r.children[0], s[:i]) and
root_match(r, s[i:]) for i in range(1, len(s) + 1))
That's right. The code is nearly identical except the index starts at one instead of zero. Ouch. Since my code worked on more complex test cases I naturally assumed that it would work on simpler cases. This goes to show that you should always check simpler cases in your own code and make sure they work perfectly.
On the bright side, I managed to ace the last test for my first 100% on a midterm at U of T. Hopefully there will be many more to come. I hope everyone is enjoying the course and I wish everyone the best of luck in their course work. Cheers!
if r.symbol == '*':
return s == '' or any(root_match(r.children[0], s[:i]) and
root_match(r, s[i:]) for i in range(len(s) + 1))
The code failed on regex_match(RegexTree('e*'), '0'), it produces an infinite loop in this case. The key here is the splicing index. My method calls itself with the same string and same regextree, which indeed produces a stack overflow of recursive calls. After investigating, I found the correct code to be:
if r.symbol == '*':
return s == '' or any(root_match(r.children[0], s[:i]) and
root_match(r, s[i:]) for i in range(1, len(s) + 1))
That's right. The code is nearly identical except the index starts at one instead of zero. Ouch. Since my code worked on more complex test cases I naturally assumed that it would work on simpler cases. This goes to show that you should always check simpler cases in your own code and make sure they work perfectly.
On the bright side, I managed to ace the last test for my first 100% on a midterm at U of T. Hopefully there will be many more to come. I hope everyone is enjoying the course and I wish everyone the best of luck in their course work. Cheers!
Monday, 18 November 2013
Python's "Private" Attributes
Today we learned something interesting in class. Apparently Python does not make use of private variables like C++ or Java does. This was a little bit unsettling to hear, since I've mostly programmed in Java and can't imagine the security risks and other problems that might arise as a result of this fact. For example, say the user is fiddling with class variables which he/she has no reason to fiddle with. Well, Danny showed us a neat solution which the creators of Python use to fix this issue. The solution is to use the built-in "property" method. I'd rather not get into the specific syntax of all of this. I would, however, like to discuss the main idea of this method and how it fixes the problem (who wants to read syntax in a blog post anyway?). Here's how we do it.
We want to define getter and setter methods just like we do in Java. However, we don't necessarily want people to use them explicitly in their code. So when a private variable is referred to in our code, we want our property method to implicitly call the appropriate class methods to do the intended work for us. To make this more clear, let's say a programmer refers to a private variable in our class and assigns a value to it. The programmer does not need to call the setter method explicitly, instead the property method would probably recognize the equal sign used for assignment and it would determine that the setter method we defined in the class would be appropriate in this case. Suppose the programmer chooses not to assign it a value, but rather uses it for output or to calculate some intermediate result. Then our property method would again search for the appropriate class method to use and it would choose the getter method in this case. The advantage in this is that we can customize our getter and setter methods so that we can enforce certain rules. For example, if the user assigns some non-nonsensical value to a private variable then we should throw an exception in this case. This is the main idea of the property method explained today in class. This is almost equivalent to information hiding in Java, so it solves this problem (although it still doesn't really enforce privacy of variables).
Now this is great and all, but something else concerns me. In Java we have private classes which are inaccessible from another class. How could such a thing not exist in Python? Well we've already learned a way to fix the issue of private variables, so perhaps there is a solution to this problem as well. I guess we shall see in the coming weeks.
We want to define getter and setter methods just like we do in Java. However, we don't necessarily want people to use them explicitly in their code. So when a private variable is referred to in our code, we want our property method to implicitly call the appropriate class methods to do the intended work for us. To make this more clear, let's say a programmer refers to a private variable in our class and assigns a value to it. The programmer does not need to call the setter method explicitly, instead the property method would probably recognize the equal sign used for assignment and it would determine that the setter method we defined in the class would be appropriate in this case. Suppose the programmer chooses not to assign it a value, but rather uses it for output or to calculate some intermediate result. Then our property method would again search for the appropriate class method to use and it would choose the getter method in this case. The advantage in this is that we can customize our getter and setter methods so that we can enforce certain rules. For example, if the user assigns some non-nonsensical value to a private variable then we should throw an exception in this case. This is the main idea of the property method explained today in class. This is almost equivalent to information hiding in Java, so it solves this problem (although it still doesn't really enforce privacy of variables).
Now this is great and all, but something else concerns me. In Java we have private classes which are inaccessible from another class. How could such a thing not exist in Python? Well we've already learned a way to fix the issue of private variables, so perhaps there is a solution to this problem as well. I guess we shall see in the coming weeks.
Wednesday, 13 November 2013
Second Assignment Automarker
I recently looked at the auto marker results for assignment number two. If the mark for the assignment is given solely for the number of test cases passed, then I would be pleased seeing as I passed all but one. But for some reason, I cannot figure out why I failed this one test case. The error message is as follows:
"ERROR: Test case: regex star of leaf correctly renounces string? ERROR: RuntimeError
maximum recursion depth exceeded in comparison"
Unfortunately I have no idea what this means. My code seems to have no flaws in it (but then again you can never be too sure) so I'm very curious as to why this test case failed. Also, it seems unlikely that my code would fail only one test case. If my logic would have been even slightly off, then this would have likely resulted in several errors rather than just one. Conversely, if my logic was spot on then I should have no errors unless there was a very very small detail which I somehow missed. Anyway, I'll talk to Danny about this error and hopefully gain some insight as to why this happened, or perhaps someone can give an explanation as to why this error occurred in the comments.
Cheers!
"ERROR: Test case: regex star of leaf correctly renounces string? ERROR: RuntimeError
maximum recursion depth exceeded in comparison"
Unfortunately I have no idea what this means. My code seems to have no flaws in it (but then again you can never be too sure) so I'm very curious as to why this test case failed. Also, it seems unlikely that my code would fail only one test case. If my logic would have been even slightly off, then this would have likely resulted in several errors rather than just one. Conversely, if my logic was spot on then I should have no errors unless there was a very very small detail which I somehow missed. Anyway, I'll talk to Danny about this error and hopefully gain some insight as to why this happened, or perhaps someone can give an explanation as to why this error occurred in the comments.
Cheers!
CSC148 Second Test
I wrote the second term test earlier today. I must say I liked it very much. The test consisted of three questions involving tree structures where we were expected to write a recursive method for each. In my opinion, a test where all the questions involve recursion can sometimes be very confusing seeing as there are two possibilities when writing a recursive function: either get the algorithm correct in five to ten minutes, or spend hours trying to find the solution (there is usually no in-between). The latter happens to me sometimes when I try to present an overly extravagant solution instead of keeping it simple. Regardless, I still like recursive problems as they induce thinking. Also, this time around the problems were very straightforward and closely related to the exercises and labs done in class. On top of that, the solutions to the problems were less than five lines long (with the exception of question one). I thought the first question was probably the most difficult. The reason I have this opinion is because two base cases were required to complete the function as opposed to just one. In addition, you have to work with a tuple as the return value instead of a single value, which is slightly more complicated. Nevertheless, I was able to complete the question fairly quickly along with the other two.
I hope you all did well on your tests, that's all for now!
I hope you all did well on your tests, that's all for now!
Thursday, 7 November 2013
Big-O Notation
We covered big-O notation in class over the past couple of weeks. There are a few things which struck me as interesting. Each function belongs to a set of functions with the same run-time performance. For example, a linear search will belong to the set of functions with O(n) performance whereas a binary search will have O(log n) performance. But technically, big-O is an upper bound on an algorithm's performance. In other words, all of these sets fit into a hierarchy. So O(log n) is a subset of functions with O(n) performance (since functions with O(log n) performance cannot possibly do worse than an O(n) function). Similarly, O(n) is a subset of functions with O(n*log n) performance. So technically, a binary search algorithm fits under O(log n), but it also fits under O(n) and O(n*log n). This is what I found somewhat strange. Can I claim every algorithm given on the upcoming midterm is O(n!) and get full marks? This seems absurd.
To address this, I would assume an algorithm is classified by its simplest performance. In other words, we say linear search is O(n) and not O(n!). I don't think this was directly mentioned in class, but it seems to be a reasonable assumption to make. This was later verified in my CSC165 lecture. Hopefully the material covered in my CSC165 class will help with big-O questions on the midterm, and maybe the material covered in CSC148 will also help with my CSC165 class (awesome timing)!
Anyway, good luck on your upcoming midterms!
To address this, I would assume an algorithm is classified by its simplest performance. In other words, we say linear search is O(n) and not O(n!). I don't think this was directly mentioned in class, but it seems to be a reasonable assumption to make. This was later verified in my CSC165 lecture. Hopefully the material covered in my CSC165 class will help with big-O questions on the midterm, and maybe the material covered in CSC148 will also help with my CSC165 class (awesome timing)!
Anyway, good luck on your upcoming midterms!
Tuesday, 5 November 2013
Second Assignment
The second assignment was due today. Fortunately, I was able to finish it several days ago. The first part of the assignment was fairly straight forward. The main challenge in this part was being able to recognize which node is the root node in a regular expression. As a human, one can immediately determine if a node is the root by checking how deep it is in the expression. Using this same logic, I kept track of the expression depth by having a running counter and adding one whenever a left bracket is reached, and also subtracting one whenever a right bracket is reached. This would be useful when checking if a node is the root (i.e. if the count is equal to one, then the node is found in the lowest depth of the expression and must be the root). This is the first approach that came to my mind. I am curious if there are others which people have implemented. Let me know in the comments.
I found the second part more challenging. The main challenge in this part (for me at least) was handling expressions with star nodes. This involved splitting up a string into several combinations of sub strings and checking that each sub string matches the RegexTree for at least one of those combinations. After spending quite some time trying to do this with loops, I was able to gain some helpful insight from Danny in lecture. For anyone who is stuck on this part, I strongly recommend following his advice and using recursion.
On an unrelated note, I mentioned a problem I had on the first test in my second blog post. After reviewing the solutions, I noticed that the __init__ method in question three was completely omitted from the class we were meant to implement. This makes much more sense, so that clears up my confusion I had for that question.
Anyway, good luck on your second assignments!
I found the second part more challenging. The main challenge in this part (for me at least) was handling expressions with star nodes. This involved splitting up a string into several combinations of sub strings and checking that each sub string matches the RegexTree for at least one of those combinations. After spending quite some time trying to do this with loops, I was able to gain some helpful insight from Danny in lecture. For anyone who is stuck on this part, I strongly recommend following his advice and using recursion.
On an unrelated note, I mentioned a problem I had on the first test in my second blog post. After reviewing the solutions, I noticed that the __init__ method in question three was completely omitted from the class we were meant to implement. This makes much more sense, so that clears up my confusion I had for that question.
Anyway, good luck on your second assignments!
Sunday, 3 November 2013
Last Exercise
So the last exercise was due Friday. Having no time to start on it until Thursday (the day after my physics midterm), I was rushing to get it done. When I actually looked at the exercise I immediately realized it was painfully easy (in fact most of part (a) had already been done for us in the insert method). I was relieved and quickly finished both parts of the exercise within 5-10 minutes. I then submitted the exercise to MarkUs. Feeling confident the next morning, I decided to check my results. All of part (a) was done perfectly, whereas part (b) had 9 errors. Huh? Well apparently the method in part (b) should return the node with the maximum value and not the value itself. The handout made this very clear, but for some reason I decided to completely disregard it anyway. I spent a good 10 minutes trying to figure out what was wrong with my code only to find that there was almost nothing wrong. I quickly changed the return value and submitted to MarkUs once more to get a perfect score. It's somewhat relieving to know that I don't need to stress out every week to finish these small exercises anymore (they are usually quite easy, but even the easiest of assignments can be rather stressful). Then again, these exercises do provide an opportunity to raise one's mark. I also find it kept me on top of course material since it forced me to refresh my memory on what we're doing each week. Well, I guess that's it for the CSC148 exercises. So long!
Thursday, 24 October 2013
CSC148 First Test
So the first test was last week, and after completing it I thought I had done fairly well. The first two questions were straight forward. The third, however, was slightly confusing. The problem was to create a "FunctionalList" class, which returns a copy of a FunctionalList using slightly altered append and sort methods. Constructing the actual class did not seem to be a difficult task. But as I started to write my constructor, I realized the implementation would be a little bit tricky (at least for me). Two possibilities came to my mind, I could either implement the class by creating an instance variable which stores all the data (i.e. a list). The second possibility was to extend the list class and make a super call in the constructor instead of having an instance of a list. I decided to go with the latter. After all a "FunctionalList" is itself a list, so inheritance seems to be the way to go here (in fact the question made this clear). But this raises another question. How would the super call in the constructor be made? Is it actually possible to create a list like so:
a = list([1, 2, 3])
Well apparently this is possible, except I have never seen it before because almost nobody ever initializes a list like this. This is one of the reasons I found this question interesting. I've never actually seen a list initialized in any other way than the quick, double square bracket notation. Anyway, if this is the case then the super call in the constructor should look like:
super().__init__(data), where 'data' is a list parameter taken by the constructor.
This looked plain weird to me, but I reluctantly wrote it on my test anyway. I wonder how other students reacted to this question. Did anybody else find themselves having the same problem, or was it just me? Perhaps there is an alternative way to solve this problem which I have not considered, let me know in the comments.
a = list([1, 2, 3])
Well apparently this is possible, except I have never seen it before because almost nobody ever initializes a list like this. This is one of the reasons I found this question interesting. I've never actually seen a list initialized in any other way than the quick, double square bracket notation. Anyway, if this is the case then the super call in the constructor should look like:
super().__init__(data), where 'data' is a list parameter taken by the constructor.
This looked plain weird to me, but I reluctantly wrote it on my test anyway. I wonder how other students reacted to this question. Did anybody else find themselves having the same problem, or was it just me? Perhaps there is an alternative way to solve this problem which I have not considered, let me know in the comments.
Saturday, 12 October 2013
Recursion & Object-Oriented Programming
Recursion is a method of solving a problem by firstly solving smaller instances of the same problem. In computer science, a recursive algorithm is one which calls on itself until a base case is reached. Recursion is very important for computer scientists, as it usually provides an elegant solution to many problems which could otherwise be very complicated to solve. For example, I often play chess and I recently found a problem which can be solved using a recursive algorithm. The problem is known as the "Eight Queens Puzzle". The problem is to place eight queens on a chessboard such that no queen can attack another queen. The recursive algorithm uses backtracking (once a conflict is found, the computer will go back to the last proper state and try to find a different combination). There are many other applications of recursion. This includes binary search, recursive data types (trees), fractals and much more!
Object-Oriented Programming (OOP) is also very important for computer scientists. An OOP language allows programmers to write classes which serve as blueprints, or templates for creating objects. This is very similar to real life. To build a radio, for example, a manufacturer would firstly create a template for the radio and then give this same set of instructions to a factory to mass produce. If the company wanted to make a different radio, then they would likely use a slightly altered version of the original template rather than constructing an entirely new template. This same idea is used in programming. Building a radio object would firstly require writing a class. Building a different radio object would require reusing the same class, but altering some of it (this is where inheritance comes in). This is highly advantageous for computer scientists as it does not require a lot of code to be repeated (much cleaner). Another advantage of OOP is information hiding (encapsulation). Going back to the radio example, an average consumer who buys a radio can use it and not worry about how the radio actually works as it comes with an interface (power button, volume control, etc.). This same advantage is present in computing. Programmers need not know how an object works, they just need to know that it does work (before OOP, coders needed to know how a piece of code works before using it). Clearly, OOP makes our lives easier when we are coding. It makes our code cleaner, reusable, readable and generally easier to work with.
Object-Oriented Programming (OOP) is also very important for computer scientists. An OOP language allows programmers to write classes which serve as blueprints, or templates for creating objects. This is very similar to real life. To build a radio, for example, a manufacturer would firstly create a template for the radio and then give this same set of instructions to a factory to mass produce. If the company wanted to make a different radio, then they would likely use a slightly altered version of the original template rather than constructing an entirely new template. This same idea is used in programming. Building a radio object would firstly require writing a class. Building a different radio object would require reusing the same class, but altering some of it (this is where inheritance comes in). This is highly advantageous for computer scientists as it does not require a lot of code to be repeated (much cleaner). Another advantage of OOP is information hiding (encapsulation). Going back to the radio example, an average consumer who buys a radio can use it and not worry about how the radio actually works as it comes with an interface (power button, volume control, etc.). This same advantage is present in computing. Programmers need not know how an object works, they just need to know that it does work (before OOP, coders needed to know how a piece of code works before using it). Clearly, OOP makes our lives easier when we are coding. It makes our code cleaner, reusable, readable and generally easier to work with.
Subscribe to:
Posts (Atom)