Monday, 14 December 2015

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.

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.

publicstaticvoid 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.

publicstaticvoid encrypt(String s)

{

System.out.print(s+" ");

if(s.equals("ab"))

System.out.println("a b");

elseif(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:

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.

…

elseif(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(intn=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(intn=0;n<3;n++)

{

for(intm=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(intn=0;n<3;n++)

{

for(intm=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(intm=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(intm=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:

…

{

introws = 3;

intcols = 3;

for(intn=0;n<cols;n++)

{

for(intm=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.

…

elseif(s.length() < 13)

{

introws = 3;

intcols = 4;

…

Now let us compute the rows and columns as suggested in the problem statement.

…

introws = (int) Math.floor(Math.sqrt(s.length()));

intcols = (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:

…

introws = (int) Math.floor(Math.sqrt(s.length()));

intcols = (int) Math.ceil(Math.sqrt(s.length()));

if(rows*cols < s.length())

rows++;

for(intn=0;n<cols;n++)

{

for(intm=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?