Author Archives: jlee3811

CS-448 Week 13 Nurture Your Passion

The pattern “Nurture Your Passion” is compelling to me as a software developer who is learning the importance of maintaining enthusiasm and dedication in a challenging work environment. It acknowledges the common struggle many developers face when their passion for the craft is interrupted by factors such as corporate hierarchies, project pressures, or negative workplace dynamics.

What I find compelling about this pattern is its emphasis on taking the right steps to protect and grow one’s passion for software craftsmanship despite adversity getting in the way. It also addresses that while external factors may be discouraging, there are still actions individuals can take to sustain their enthusiasm and commitment to their craft.

I have personally experienced times when my passion for software development wavered due to demanding project deadlines, different obstacles, and lack of recognition. However, this pattern reminds me that while it may seem tough at times, my passion is worth preserving and that I have the tools to nurture it.

One of the aspects this pattern provides that resonates with me is the importance of setting boundaries between work and personal life. Prioritizing self-care and making time for out-of-work activities that help rejuvenate my passion allows me to maintain a healthy balance and prevent burnout.

The pattern also underscores the significance of continuous learning and seeking out like-minded peers for support. Professional development opportunities as well as connecting with colleagues who share my enthusiasm for software development can provide me with valuable inspiration and encouragement.

Reflecting on this pattern has reinforced my belief in the adaptability and resilience of software developers. It has reminded me that while external factors can influence the work environment, I have the power to carve my own experience and continue my passion for the craft.

If anything, this pattern has confirmed my intended profession and pursuing a career in software development. There have been times that I considered trying a different career path, but reflecting on this pattern has showed me that my belief and dedication are essential qualities for success in this field, and that overcoming challenges can lead to personal growth and fulfillment.

Overall, I agree with the principles outlined in this pattern. It serves as a reminder that passion is not something that is given, but rather it is something that should be nurtured and protected, especially when facing adversity. By embracing this mindset and taking the right steps to maintain my passion, I am confident that I can thrive as a software developer despite any obstacles I may encounter.

3. Walking the Long Road | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Week 12 Study the Classics

The pattern “Study the Classics” highlights the importance of remembering and familiarizing yourself with foundational topics and the timeless concepts in software development. This is especially true for individuals with practical or self-taught backgrounds. It suggests that rather than feeling overwhelmed by the vast array of literature available, one should focus on reading books that have stood the test of time and continue to offer very valuable insights into the field.

What I find compelling about this pattern is the emphasis on the enduring relevance of classic texts in a rapidly evolving industry. It underscores the notion that while technologies may change, fundamental principles and concepts often remain consistent. Studying these fundamental texts will allow individuals to gain a deeper understanding of underlying principles that drive software development, which enables them to make more informed decisions and adapt to new technologies more effectively.

This pattern influenced my perspective on professional development and has reinforced the importance of continuous learning and reflection in my intended profession. Rather than focusing solely on the latest trends or technologies, I now see the value in investing time to study classic texts that offer timeless wisdom and insights.

While I agree with the overall premise of the pattern, I also recognize the possible limitations to solely relying on classic texts for learning and development. The field of software development is dynamic and multifaceted, and it’s essential to stay abreast of emerging trends and technologies. Therefore, studying the classics is valuable, but it should be complemented by ongoing learning and experimentation to ensure relevance and adaptability in today’s fast-paced industry.

In summary, “Study the Classics” underscores the importance of studying foundational texts in software development to gain a deeper understanding of timeless principles and concepts. While this pattern has reinforced the value of classic texts in my professional development, I also recognize the need for a balanced approach that incorporates both classic wisdom and ongoing learning to navigate the complexities of modern software development effectively.

6. Construct Your Curriculum | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Sprint 2 Retrospective

Merge branch ‘verify-pipeline-tests’ into ‘main’ (490e4d29) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / General · GitLab

Last sprint teams were focused on changing the pipeline tests and extensions for certain repositories and so this sprint teams were assigned to verify the correct linters and extensions were added as well as running the correct tests when committing the changes to Gitlab. ReportingSystem General does not build a Docker image nor does it have release number. For this repository, we only need to run the linter pipeline tests and ensure that they all passed with no errors.

Merge branch ‘verifying-that-reportingsystem-10’ into ‘main’ (e7755051) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / Documentation · GitLab

Like ReportingSystem General, the Documentation repository did not need to run through all the pipeline tests in Gitlab such as the build, test, or release stages. This means that we only had the linter tests run through the pipeline when committing any changes to this repository.

Merge branch ‘jlee1999/verifying-that-generatewcfbreportfrontend-38’ into ‘main’ (6e5d198e) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / GenerateWCFBReportFrontend · GitLab

GenerateWCFBReportFrontend is different than the other two repositories that I worked on as it does require pipeline tests such as build, test, and release. Originally, the tests were passed with a warning, which was okay at the time, but our goal was to ensure all the tests passed with no warnings.

Files · jlee1999/research-functionality-122 · LibreFoodPantry / Client Solutions / Theas Pantry / GuestInfoSystem / GuestInfoBackend · GitLab

Once I completed the verification related issues in our sprint backlog, I shifted focus on the first steps of hot reloading GuestInfoBackend servers for development mode. First, I researched what the command “nodemon” is and how I can properly integrate it into the code. This command allows for the server to restart after any changes are made in the folders specified prior. I created a separate folder in this branch called “express-example” with a simple JavaScript file as well as the package.json file and any other required files needed. This allowed me to mess around with the functionality of the command without completely rearranging the main branch.

feat: merge branch ‘jlee1999/edit-scripts-to-allow-123’ into ‘main’ (f145cdc1) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / GuestInfoSystem / GuestInfoBackend · GitLab

After messing around with nodemon in the sample branch, I was ready to implement it to the main branch of GuestInfoBackend. I was having trouble making sure the nodemon command works while also ensuring the pipeline tests passed. If I was able to get the pipeline tests to pass then the functionality of nodemon was not working, and if nodemon was working then the pipeline tests would fail. I was struggling trying to figure out why this issue kept occurring, but after a while I was able to get both the functionality to work along with the pipeline tests to pass simultaneously. The cause of this problem was because the repository was using both npm and yarn when building the pipeline tests which caused problems. I decided to stick with strictly npm for the repository which then fixed the issue.

Files · update-checkinventory-29 · LibreFoodPantry / Client Solutions / Theas Pantry / InventorySystem / CheckInventoryFrontend · GitLab

I did not get to work too much on CheckInventoryFrontend this sprint but was able to get a general idea of some issues that could be arising. I noticed that this repository also has both npm and yarn together which has caused some issues with the pipeline tests. Hopefully the next sprint my team and I can solve this issue and provide a simple and working frontend for the InventorySystem.

This was the second sprint for the team, and I believe that it went better this time around compared to the first sprint. I felt more comfortable with the whole set-up of our sprints and the team was able to get the work done we needed to. Around half of the issues this sprint revolved around verifying pipeline tests and extensions in repositories that will help in the future to allow people to focus on bigger parts of the project with more ease. While I worked on 3 of the issues around verifying pipeline tests, our team had 5 overall which made it easy for us to communicate what worked for the other issues. The continued work in Gitpod/Gitlab has gotten me even more comfortable committing changes along with merging branches. There was only one issue that I messed up the commit message when squashing the commits for GuestInfoBackend, but it did not affect the overall functionality of the repository and the pipeline tests were still able to pass.

While I would like to say everything worked out perfectly during this sprint, it would be a lie. There were some small hiccups in progression through this sprint, but I would say it was less severe than the issues holding us back last sprint. I believe the higher level of comfort in the progress helped allow the team to get passed any hiccups. The biggest issue I was running into was during the GuestInfoBackend issue about hot reloading the servers. I touched on it earlier, but the combination of npm and yarn in the repository was causing me the most issues during this sprint, but I was able to solve it before the end of the sprint which was the goal.

Overall, I believe the team can still improve on communications in some respects, but I would say we were all on the same page more often this sprint compared to last. I would make sure I would notify the team in Discord about any issues I was running into or if I needed a review on a merge request, and the team would be able to see it and help continue the progress of the sprint. I did a better job this sprint focusing on one issue at a time rather than trying to work on multiple issues at once.

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Week 9 Be the Worst

The “Be the Worst” pattern encourages you to surround yourself with individuals who excel in their craft, even if it means being the least skilled person in the group. By doing this, you engage yourself in an environment that fosters continuous learning and growth, as it exposes individuals to new perspectives, challenges, and techniques that may not occur otherwise.

I find this pattern both interesting and thought-provoking because it challenges the traditional notion of seeking out environments where one is the most skilled or experienced. Instead, it encourages the embracement of discomfort and for individuals to seek out opportunities for improvement, even if it means initially feeling inadequate or out of place.

Embracing this pattern has caused me to reconsider how I approach my career and professional development. I have been inclined to seek out roles or teams where I felt confident in my abilities in the past, always fearing the possibility of struggling or being overshadowed by more experienced peers. However, I can see the value in seeking out environments where I am the weakest member after my reflection on this pattern. This will give me the chance to push myself to learn and grow at a faster pace, ultimately becoming a stronger and more versatile developer.

While I generally agree with the principles outlined in this pattern, I do acknowledge that it may not be suitable for everyone or in every situation. Some individuals may thrive in environments where they are the most skilled, finding comfort and confidence in their expertise. Additionally, constantly being the least skilled member of a team can be emotionally challenging and may require a certain level of resilience and self-confidence to navigate successfully.

Overall, the “Be the Worst” pattern serves as a reminder of the importance of humility, curiosity, and continuous learning in one’s professional journey. By seeking out opportunities to surround oneself with individuals who are better than them, individuals can unlock their full potential and achieve greater success in their chosen field.

4. Accurate Self-Assessment | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Week 8 Long Road

The pattern of “The Long Road” highlights a common dilemma faced by aspiring software developers in today’s fast-paced industry. It contrasts the culture of instant gratification and superficial success with the desire to pursue mastery and craftsmanship in software development.

What intrigues me about this pattern is the disconnect between the long-term goals of becoming a proficient software developer and the short-term pressures to prioritize immediate financial gain and career advancement. Especially early in their careers, developers find themselves torn between these conflicting expectations while navigating through the complexities of the industry and trying to establish themselves.

This pattern resonates with me personally because it highlights the internal struggle I have faced in my own journey as a software developer. While I have not had any significant offers in the field of work yet, I know that true fulfillment in this line of work comes from the pursuit of excellence, continuous learning, and honing one’s craft over time. With my focus on excellence in my personal craft, it will pay off for me in the future later down the road.

The notion that the lessons and wisdom of seasoned software developers often goes unheeded in an industry that is constantly chasing the next big thing I found thought-provoking. The lack of knowledge transfer between generations of developers leads to the repetition of past mistakes and the reinvention of the wheel.

This pattern has caused me to reconsider my approach to my intended profession and how I want to work. Rather than falling into the external pressures and expectations, I am more committed to stay true to my passion for software development and prioritizing personal growth and mastery over short-term gains.

While I understand the advice to prioritize financial stability and career advancement, I do not completely agree with the notion that slow and steady skill-building is somehow less valuable or important. I believe that the pursuit of mastery is what ultimately sets exceptional developers apart and leads to long-term success and fulfillment in this field. I remain steadfast in my commitment to honing my skills, embracing lifelong learning, and striving for excellence in my craft, regardless of the trends or societal expectations.

3. Walking the Long Road | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Week 7 Learn How you Fail

The pattern being examined is “Learn How to Fail” and it emphasizes the inevitability of failure in everyone’s life. It also suggests that failure is not a sign of incompetence but rather an opportunity for growth and self-awareness. This encourages individuals to analyze their failures, understand the underlying patterns, and address them effectively without dwelling on past mistakes or striving for perfection.

This pattern resonates deeply with me due to the universal nature of failure and brings light to the fact it is a natural part of the learning process. I am one that values continuous self-improvement and find the idea of embracing failure as a catalyst for personal growth both empowering and liberating. Instead of viewing failure as inadequate, it is seen as an invaluable source of feedback that can inform my future decisions and actions.

One of the more interesting and useful aspects of this pattern is its emphasis on self-reflection and self-awareness. The pattern provides a practical framework for introspection and self-discovery by encouraging individuals to identify recurring patterns, habits, and behaviors that contribute to their failures. This will help individuals to make more informed choices and develop strategies to lessen their weaknesses effectively.

This pattern has certainly influenced the way I think about my intended profession and how I approach my work. Failure is now viewed as an opportunity for learning and development rather than a setback or source of shame. I intend to face challenges and setbacks during my career path and handle them with resilience, knowing that they will contribute to my overall growth and success.

While I do not disagree with the general message of this pattern, there can be cause for concern given how you might handle failure. It is important to acknowledge and learn from failure, it is just as important to celebrate successes and achievements. Failure can provide valuable lessons but can also overshadow or diminish one’s accomplishments in the search of self-improvement. Balancing both self-reflection and self-affirmation is crucial for maintaining a healthy mindset and sustained motivation.

In conclusion, the pattern underscores the importance of embracing failure as a steppingstone to personal and professional growth. By cultivating self-awareness, learning from mistakes, and approaching challenges with resilience, individuals can navigate their professional journeys with confidence and adaptability.

5. Perpetual Learning | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Sprint 1 Retrospective

Merge branch ‘jlee1999/vscode-issue’ into ‘main’ (4ca23d2d) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / General · GitLab

One of the problems I ran into came when combining issues within the same branch. For instance, within this one merge request to ReportingSystem General, there were three issues that were worked on which ruined the commit messages when squashing the branch. The commit messages would only reflect one of the issues being fixed rather than three separate commit messages that are associated with a particular issue.

Merge branch ‘jlee1999/gitpod-setup’ into ‘main’ (c2ec7bc6) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / Documentation · GitLab

This issue was in ReportingSystem Documentation and the task was to change the folder “commands” to “bin” to avoid any syntax errors within the code. This also required the refactoring of the code/files wherever “commands” was located so that it was changed to “bin”.

Merge branch ‘alexjs_linter_add’ into ‘main’ (6f9c803f) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / ReportingBackend · GitLab

This issue was in ReportingBackend and asked to add AlexJS linters to the pipelines. This required enabling the AlexJS test in gitlab-ci.yaml file so that the test would run through the pipelines when committing any changes.

Merge branch ‘jlee1999/alexjs-linter’ into ‘main’ (443debe1) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / ReportingSystem / GenerateWCFBReportFrontend · GitLab

This issue was also related to adding AlexJS linters to the pipeline of GenerateWCFBReportFrontend to check the indicated files for any problems.

Merge branch ‘jlee1999/gitpod_setup_branch’ into ‘main’ (636896d3) · Commits · LibreFoodPantry / Client Solutions / Theas Pantry / InventorySystem / InventoryIntegration · GitLab

This issue revolved around adapting InventoryIntegration to allow VSCode to work in Gitpod. There were some complications with this issue since this was the first issue I worked on and not all the pipeline tests passed when committing the changes.

For the first Sprint I think that there were a few things that went well during the process. I made sure that I would not get stuck on one issue for a long period of time so that way it did not hinder our overall progress through the other issues we needed to work on. A lot of the issues we were assigned revolved around the same thing, which made it easier to fix once solving the first issue in a repository. For example, setting up AlexJS linters to pipelines was required for us in multiple repositories in Thea’s Pantry, so once we figured out how to solve the first issue it was easy to carry over the same solutions to the other required repositories. The more I started working in GitLab and Gitpod/VSCode, the more comfortable I got with the layouts. This will continue to help through later issues and sprints as there is still more I can learn which will make it easier to fix different issues.

While there were positives to take away from my first experience in a Sprint setting, there were some things that did not work as well that hindered the overall progression of the project. At first, I forgot to create a separate branch when working on my first issue but then realized after committing the changes to the main branch. Luckily it was nothing too major that I was changing so it did not affect the entire repository. I personally would get caught up in too many issues at once and bounce around through multiple at a time.

I think the main thing that the team can improve on for the next sprint is more communication amongst everyone. The more communication there is, the easier it will be to identify what each team member is working on at the time. Letting each other know when there is a merge request to be reviewed will make it quicker to get through each issue and identify any mistakes that might arise in merge requests. As an individual, I can improve my focus on one issue at a time rather than working on multiple issues at once.

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Apprenticeship Patterns Chapter 2-6 Introduction

Chapters 2-6 in the Apprenticeship Patterns textbooks goes over the specific different practices and patterns that software developers use in order to grow in their field.

Chapter 2 talks about the values of “emptying the cup” as an apprentice. Embrace the beginner’s mindset with The White Belt. Overcome roadblocks with Unleashed Enthusiasm. Acquire concrete skills in specific technologies to explore advanced patterns. Avoid complacency by systematically broadening your tech skills. Expose your ignorance to focus on learning needs. Confront and flex your knowledge muscles. Take audacious tasks to learn and grow. Retreat into competence when overwhelmed, then gather yourself to ascend further.

Chapter 3 explains how many people are walking the same path even when they seem to be on a different path. In other words, the experts that know more than you in the work field went through the same struggles (walked the same path) as you are to get to where they are now. It explains the story of Dave as he continues his journey in software development with a sense of assurance. However, his interactions with highly skilled hackers in online communities and face-to-face collaborations with high level developers humbled him and fueled his desire to learn. As he delved deeper into side projects and consuming learning material, Dave realized the vastness of knowledge available and the continuous learning process inherent in software development. While there is a considerable gap between his skills and those of seasoned developers, Dave found solace in the shared journey towards mastery that all developers embark on.

Chapter 4 introduces the concept of letting go of perceived competence and allow yourself to recognize that there is way more to learn and more to travel down the Long Road. The rapid learner faces the risk of becoming complacent in their success within their current environment which could stagnate their growth. It is crucial for such individuals to acknowledge the broader landscape of opportunities beyond their immediate sphere and to remain humble in the face of their accomplishments. The focus should not be on surpassing others but on personal growth and improvement, recognizing that the journey towards mastery is ongoing and that collaboration and mutual support are integral to this process.

Chapter 5 emphasizes the concept of learning and, especially for apprentices, how it is critical these patterns be applied early on in their journey. The essence of apprenticeship in software development revolves around perpetual learning and effective communication. Apprentices must constantly seek opportunities to replace ignorance with skill, not only acquiring concrete technical abilities but also in developing the ability to learn itself. These concrete learning activities such as “Expanding your bandwidth” and “Breakable Toys” pave the way for deeper self-discovery, leading to the importance of reflecting on work. sharing knowledge, creating feedback loops, and understanding personal weaknesses to transition successfully from apprentice to journeyman and eventually master craftsman.

Chapter 6 talks about the constant time and effort that should be put into studying to further develop your learning curriculum. In today’s age digital age, access to vast amounts of information is easy, thanks to the Internet and handheld devices. While blogs and online resources offer valuable content, the wisdom found in traditional books by experts like Jerry Weinberg and Kent Back is irreplaceable. Incorporating reading into an apprenticeship is crucial for success, allowing individuals to construct their own learning curriculum.

2. Emptying the Cup | Apprenticeship Patterns (oreilly.com)

3. Walking the Long Road | Apprenticeship Patterns (oreilly.com)

4. Accurate Self-Assessment | Apprenticeship Patterns (oreilly.com)

5. Perpetual Learning | Apprenticeship Patterns (oreilly.com)

6. Construct Your Curriculum | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Apprenticeship Patterns Chapter 1 Introduction

Chapter 1 of the Apprenticeship Patterns textbook introduces the principles and ideals of software craftsmanship. This is an approach to software development that emphasizes the importance of writing high-quality code and delivering value to customers. Some key aspects of software craftsmanship include quality code, continuous learning, collaboration, focus on customers, and testing/quality assurance. Following these key aspects helps promote professionalism, excellence, and pride in one’s work as a software developer. This textbook draw inspiration from several highly skilled individuals that were interviewed for the book. Their values helped give meaning to what it means to be an apprentice, journeyman, or a master.

Apprentices typically have the attitude that there is always a better/smarter/faster way to do what you just did and what you are currently doing. Apprenticeship is the beginning of the journey as a software craftsman. One of the most important things needed is the ability to learn and grow for yourself, and then attention from your peers and experienced developers will follow. As you progress through the stages of craftsmanship, it is important to retain the knowledge and attributes gained from the previous stages. Journeyman will continue to focus on growth in their craft, but also focus on connections between practitioners, within and outside the team. Traditionally, a journeyman will move from master to master and sharing ideas amongst the various teams. They are focused on building a portfolio of applications that demonstrates the progress in their craft. Their responsibilities are wider than those of an apprentice, which means their failure can do more harm. Mastery still retains the previous knowledge of the other stages but adds the focus of moving the industry forward. It involves taking skill and translating it into a way to enhance the skills of others.

Apprenticeship is the fundamental learning method through practical experience under the guidance of a skilled mentor or master practitioner. It involves a structured training program where individuals work alongside experienced professionals to acquire the knowledge, skills, and competencies to help further their learning. Key characteristics of apprenticeship include hands-on learning, mentorship, structured curriculum, progression, certification, and industry alignment. Overall, apprenticeship offers a valuable pathway for individuals to gain practical experience, develop professional skills, and pursue careers in the desired field.

1. Introduction | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.

CS-448 Week 1

This week I learned about one of several apprenticeship patterns from the “Apprenticeship Patterns” book by Dave Hoover and Adewale Oshineye, the White Belt. The purpose of this is to embrace the state of being a beginner, with no pressure of being an expert. It also states that every step should be embraced with an open mind and to set your previous knowledge aside when taking on new situations. The name “White Belt” comes from the prestige rankings of karate belts, with the white belt being for beginners. As a white belt, you do not carry any previous knowledge about the situation and are forced to learn the way, rather than black belts who know the way. As a programmer, learning a language and becoming proficient in that language is a great feeling. This can create a stubbornness in the person to be unwilling to go through the learning process again in hopes of not making a fool of themselves again when making a mistake.

While these principles are referenced in terms of computer science, they can be applied in general situations as well, especially the White Belt pattern. The well-known saying “you learn something new every day” applies to this pattern as it states that everyone still has room to improve/learn and no one knows everything, even if you feel like you do. This is important to remember when attacking new situations so that you do not limit yourself to possibilities you may not know about. It is something that people might not feel comfortable with at first due to the learning curve that comes with it, but there is always something to take away from failing the first time.

I believe this will be an important principle to follow when going into the professional world. As a software developer, there are a lot of different programming languages that can be learned with their own benefits. While some programming languages are similar, others can be harder to learn with syntax that may not align with each other. Knowing more languages allows for more opportunities when looking for a job. However, it is not easy to learn multiple languages especially when they look completely different. This is where, as a leaner, you must unlearn what you already learn.

2. Emptying the Cup | Apprenticeship Patterns (oreilly.com)

From the blog CS@Worcester – Jason Lee Computer Science Blog by jlee3811 and used with permission of the author. All other rights reserved by the author.