2017-02-19
Readability Conscious
blogentry, programming, todayilearned, c
blogentry, programming, todayilearned, c
Featured Image - "voynich" by D.C.Atty, used under CC BY / Dropped Quality to 60% from original - It's a featured image since nobody can figure out what's written in Voynich Manuscript. ;)
I've been solving HackerRank problems lately. HackerRank provides many coding problems.
[caption id="attachment_277" align="alignright" width="300"]
HackerRank Discussions[/caption]Each problem has a discussion forum to post algorithms and sometimes answers. After solving each question, I compare my answers with those of others.
After reading Clean Code by Uncle Bob, and listening to Coding blocks podcasts with titles that begin with "Clean Code - ", I decided to write a more readable code even for solving a simple question.
I noticed that many people ask in discussion what the code does. Why would I have to write a code, which would require an explanation? Could I write more readable code that anyone can understand?
There is a question titled, Balanced Brackets, which asks if brackets are closed or not. I see many answers that requires much thinking even though code is short.
Here is my solution for the question.
1public static void Main()2{3 int caseCount = Convert.ToInt32(Console.ReadLine());4 List<string> testCases = GetTestCases(caseCount).ToList();5
6 PrintMatchingBrackets(testCases);7}
Basically, all the code does is , get test cases and print matching brackets.
Here is the code for getting test cases; It just reads input from console.
1private static IEnumerable<string> GetTestCases(int caseCount)2{3 for (int i = 0; i < caseCount; i++)4 {5 yield return Console.ReadLine();6 }7}
Now, PrintMatchingBrackets simply delegates work to another function, which is aptly named, PrintMatchingBracket.
1private static void PrintMatchingBrackets(List<string> testCases)2{3 foreach (string testCase in testCases)4 {5 Console.WriteLine(HasMatchingBrackets(testCase) ? "YES" : "NO");6 }7}
Now, HasMatchingBrackets is a bit complicated but I abstracted most of low level code into separate functions or lambdas with names describing what each code does.
1private static bool HasMatchingBrackets(string testCase)2{3 // Matching brackets should have even number of brackets.4 Func<string, bool> hasOddLength = text => text.Length % 2 == 1;5 if (hasOddLength(testCase)) return false;6
7 var openingBracketMap = GetOpeningBracketMap();8 var closingBracketMap = GetClosingBracketMap();9
10 Func<char, bool> isOpeningBracket = c => openingBracketMap.Keys.Contains(c);11 Func<char, bool> isClosingBracket = c => closingBracketMap.Keys.Contains(c);12
13 var stackSize = testCase.Length / 2;14 Stack<char> stack = new Stack<char>(stackSize);15 Func<char, bool> isLastOneInStackMatching = c => stack.Peek() == closingBracketMap\[c\];16
17 foreach (char c in testCase)18 {19 if (isOpeningBracket(c))20 stack.Push(c);21
22 if (IsStackEmpty(stack) && isClosingBracket(c))23 return false;24
25 if (isClosingBracket(c) && isLastOneInStackMatching(c))26 stack.Pop();27 }28
29 return IsStackEmpty(stack);30}
I especially thought that adding isOpeningBracket and isClosingBracket increased the readability quite much since it describes my intention. Had I used openingBracketMap.Keys.Contains(c) inline, reader would have to try to figure out why I put that code in there. Describing the intention with the lambda name makes it clear.
If I really wanted to go further, I could have moved all bracket logic into another class and used a strategy pattern and whatnot, but I thought that it'd be too much for this simple problem. I had to draw a line somewhere. If another question comes up where this functionality is needed, then I'd refactor it into a library.
The above source code is available on GitHub.
I read DotNet/CoreFX code on GitHub and it seems like it was written from top-down;Meaning top level code was written as just calling functions with meaningful names and just implement the lower level functions by breaking them down as small as possible thus making each function not spanning more than a screen.
I've been trying that approach and it seems to be making the code much more readable, modular, and maintainable since I can just call small functions elsewhere in many different functions, which reduces code duplication.
I approached the Balanced Bracket problem the same way. I wrote the "Main" code first, without any implementation and just simply implemented functions along the way.
Solving coding questions and writing readable code helps me understand the question better and let me find an error in the code much easier. I will improve my programming skill so that other people can understand it by helping them read it like a book.