Monday, 14 December 2015

Code like a sloth

Code like a sloth


A programmer’s guide to incremental coding


Set a timer (in your mobile) for 21 minutes.

We will do a programming puzzle now. It is from HackerRank, a popular competitive programming site. While we are at it, when the timer fires; we will stop what we are doing (bang!) and come back to this blog.

Get on with this puzzle; https://www.hackerrank.com/challenges/encryption. You may solve it in the editor in the page or use the development tool of your choice.

***

You are back.

Answer the following questions. If the answer to any question is “No”, skip the rest of the questions and read on.
1.     Does your code compile (as applicable)? Any runtime error?
2.     Do you know what all cases/scenarios are handled by your code? Are you highly confident about it?
3.     Do you know how your code may behave for any unhandled case/scenario? Are you highly confident about it?
4.     Does the code pass the test cases when you click the “Run Code” button?
5.     Go ahead and submit your code. The site will run more tests on your code now. Are the results as expected?

Coding in a sprint/iteration is also somewhat like this exercise. Software product development is essentially problem solving in nature. Coding is like solving a puzzle. I love solving puzzles and that is why I am a programmer. In the sprint, the programmer starts developing an increment, assuming a solution. The assumption can be tested only by coding it; thus the solution evolves while coding. Somewhere during this process, the time box of the iteration ends. At this time, the only question that matters is how well done is your increment? We would understand later in this discussion that it is not really about how much of the planned work we completed or what percentage (ugh!) of the feature we coded.

Did the titular reference to the sloth get you curious? Let me explain the use of this metaphor.

The sloth is a much misunderstood animal. Many would think about it as a lazy dozer. It may be surprising to know that they don’t sleep that much, but only for 8 or 9 hours a day. Being still for most part of the day is simply, the defense against their worst predator – the eagle.
The eagle is a very formidable predator, swift and with a keen eye for a prey. Being inconspicuous is probably your best defense against something that can dive bomb and snatch you off a branch, just like that. They have some adaptations that help them to be unseen, like their fur which is very suitable for algal growth. From the sky, they would probably look like some dull branch or fruit. Their lazy, yet amazingly simple defense has been so effective that the sloths has been around for 100 million years or so (to put that in perspective, some of our earliest known ancestors are only 6 million years old) and did not require any further adaptation for natural selection all this time.

Incremental coding is like the sloth; mostly misunderstood, although amazingly simple in its scheme. 

Let us attempt the same puzzle by doing incremental coding. I have grouped the activities in a sequence of six steps as follows:

1.    Fail loud and clear


Make clear decisions about what scenarios are included and what are excluded. In incremental development, it is more important to decide what not to do than what to; be it for the product, the iteration or the increment.

Apple decided not to support SD cards in iPhone. Contrast it against the multiple usability issues reported on android versions till date related to SD card support. The android community also agrees that their support is not (yet) full and promises some improvements in the Marshmallow version.

Once we decide about the exclusions, define our behavior it to it clearly. It is suggested to fail loud and clear, like throwing an exception in Java.

As we start, let us exclude everything and fail with a special string, "_E". Don't forget to test it with a simple test case like encrypt("xyz") and check for the expected failure.

public static void encrypt(String s)
    {
             System.out.println("_E");
}

2.    Start with the simplest case


We will start with the simplest case. In this case, a two letter string would be good enough to start. We will implement it in the simplest way (as shown below) and yes! we would test it by calling encrypt("ab") and expecting "a b" as the output.

if(s.equals("ab"))
        System.out.println("a b");
    else
        System.out.println("_E");
    …

3.    Build upon it


Now let us build upon our "early win".

Let us implement encrypt("cd"), again with a simple hack.

public static void encrypt(String s)
    {
             System.out.print(s+" ");
         if(s.equals("ab"))
                    System.out.println("a b");
         else if(s.equals("cd"))
                    System.out.println("c d");
         else
                    System.out.println("_E");
}

We can use the same approach for any other tow-lettered string. However, we know there are a large number of such combinations to be handled in if-else. So we refactor the code as shown below. (Note: I have retained the commented code to illustrate the guideline of don't burn the bridges. We can fallback to the commented code if we get stuck.)

/*if(s.equals("ab"))
             System.out.println("a b");
       else if(s.equals("cd"))
             System.out.println("c d");*/
       if(s.length() == 2)
       {
             System.out.println(s.charAt(0)+" "+s.charAt(1));
       }
       else
             System.out.println("_E");
       …

A 3 lettered string would be encoded in a 2x2 matrix. Thus the first column would be char[0] followed by char[2] and the next column will have only char[1]. We can reuse the code for the two lettered string and extend it as follows:

       …
       else if(s.length() == 3) 
       {
             System.out.println(s.charAt(0)+""+s.charAt(2)+" "+s.charAt(1));
       }
       …

And so on for a four-lettered input ...

       …
else if(s.length() == 4)    
       {  
System.out.println(s.charAt(0)+""+s.charAt(2)+""+s.charAt(1)+""+s.charAt(3));
       }
       …

... and a five lettered input ...

       …
       else if(s.length() == 5) 
       {
 System.out.println(s.charAt(0)+""+s.charAt(3)+" "+s.charAt(1)+""+s.charAt(4)+" "+s.charAt(2));
       }
       …


... and don't forget to test each case.

We notice that the code handling the five-lettered input is a rather long line and we could refactor it so as to print the output column-wise, as shown below:

             …
             System.out.print(s.charAt(0)+""+s.charAt(3));
             System.out.print(" ");
            
             System.out.print(s.charAt(1)+""+s.charAt(4));
             System.out.print(" ");
            
             System.out.print(s.charAt(2));
            
             System.out.println("");
             …

4.    Observe the patterns


Let us reuse the code which handles the five lettered string and enhance it to handle the six lettered string.
else if(s.length() == 6)    
    {
        System.out.print(s.charAt(0)+""+s.charAt(3));
        System.out.print(" ");
            
        System.out.print(s.charAt(1)+""+s.charAt(4));
        System.out.print(" ");
            
        System.out.print(s.charAt(2)+""+s.charAt(5));
            
        System.out.println("");
 }
 …

We observe that the above code prints each column. The characters in each column also follows a pattern; viz. char[0] and char[0+3] for the 0th column, char[1] and char[1+3] for the 1st column etc. We refactor the code based on this pattern:


for(int n=0;n<3;n++)
    {
        System.out.print(s.charAt(n)+""+s.charAt(n+3));
        if(n<2)
            System.out.print(" ");
    }
    …


We also observe the pattern that the maximum number of characters in a column is equal to the number of rows (2 in this case). We refactor again based on this pattern.

      
       for(int n=0;n<3;n++)
       {
             for(int m=0;m<2;m++)
             {
                    System.out.print(s.charAt(n+m*3));
             }
             if(n<2)
                    System.out.print(" ");
       }

When we reuse this code for a seven lettered input, we would get an OOB exception as shown below. This is because the code would try to access charAt(7).

for(int n=0;n<3;n++)
     {
           for(int m=0;m<3;m++)
           {
                  System.out.print(s.charAt(n+m*3)); --> OutOfBounds exception!!
           }
           if(n<2)
                  System.out.print(" ");
}

We can avoid the exception with a simple bounds check.


for(int m=0;m<3;m++)
    {
          if(n+m*3 >= 7)
                 break;
          System.out.print(s.charAt(n+m*3));
    }


5.    Combine logically


To handle an eight-lettered input, we would refactor the bounds check as below.

for(int m=0;m<3;m++)
             {
                    if(n+m*3 >= s.length())
                          break;
                    System.out.print(s.charAt(n+m*3));
}

We could see that strings of 7 <= length < 10 will be encoded using a 3x3 matrix and we can refactor the code for all these cases as follows:
    {
             int rows = 3;
             int cols = 3;
             for(int n=0;n<cols;n++)
             {
                    for(int m=0;m<rows;m++)
                    {
                          if(n+m*cols >= s.length())
                                 break;
                          System.out.print(s.charAt(n+m*cols));
                    }
                    if(n<cols - 1)
                          System.out.print(" ");
             }
            
             System.out.println("");
       }
       …  

       We will reuse the code to handle strings of 10 <= length < 13 (which needs a 3x4 matrix) by only changing the number of columns.
else if(s.length() < 13)    
       {
             int rows = 3;
             int cols = 4;
       …

       Now let us compute the rows and columns as suggested in the problem statement. 
       …
       int rows = (int) Math.floor(Math.sqrt(s.length()));
       int cols = (int) Math.ceil(Math.sqrt(s.length()));
       …
   
      However, to handle a string of 13 characters, we will have adjust the rows as follows: 
       …
       if(rows*cols < s.length())
             rows++;
       …

      Thus the logic of our encrypt function would have evolved as follows:

      
       int rows = (int) Math.floor(Math.sqrt(s.length()));
       int cols = (int) Math.ceil(Math.sqrt(s.length()));
       if(rows*cols < s.length())
             rows++;
       for(int n=0;n<cols;n++)
       {
             for(int m=0;m<rows;m++)
             {
                    if(n+m*cols >= s.length())
                          break;
                    System.out.print(s.charAt(n+m*cols));
             }
             if(n<cols - 1)
                    System.out.print(" ");
       }
            
       System.out.println("");
       …


     And yes! we would handle the spaces also ...

       s = s.replaceAll("\\s", "");

6.    Reflect


The programmer may code only for half of the time; in the other half, one should reflect. Reflection may refer to any activity in which the programmer re-looks at the code including testing, refactoring or pairing.

In our example above, let us reflect by adding some more test cases, including some long strings and some special strings (only white spaces etc.).

Let us also refactor, the loop looks like a good place to start with.

To test your understanding of incremental coding, take another programming puzzle. Set the timer and start coding. When the timer fires, answer the set of questions which we have seen in the beginning of this blog. And ask yourself, is your increment well done?