Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.

Tuesday, January 17, 2012

Buzz 5 - The FizzBuzz Hypothesis

For the Buzz entries I intend to write something more involved. These entries will have a FizzBuzz solution, but the text will only be about FizzBuzz. And if you check various dictionaries the act of talking 'about' FizzBuzz gives me a lot of latitude to talk about practically anything tangentially related.

Today, however, I intend to stay fairly close to FizzBuzz itself. What I want to talk about is something I will call "The FizzBuzz Hypothesis:"
Given a particular language feature or programming technique, if it cannot be shown as an integral part of a FizzBuzz solution then the typical programmers will not be able to use it correctly.
This statement is worth deconstructing from multiple angles. First there is the logical structure of the hypothesis:
¬r → ¬q
where r is the ability to express the programming language feature or technique in the form of FizzBuzz and q is typical programmers using it correctly. This also implies something by negation. If typical programmers are using a language feature correctly then there must be some way to make it integral to a FizzBuzz solution, and if it can be proven that there is no such FizzBuzz solution then clearly the typical programmer isn't using it correctly, all other evidence notwithstanding. Also note the word choice here. I did not say properly or effectively, I said correctly. There are proper and effective ways to do things that are not correct. I am reminded of this every day I watch my children do chores around the house.

There is also the question of what I mean by the typical programmer. Since there is a high amount of variability on the high end of the "mean average programmer" the mean pulls up the average such that more than half of the programmers are less than average. There is also the possibility of a "median" programmer, where half the programmers are better or worse. This too has its faults, because that means that half of the programmers may not be able to even program a FizzBuzz solution. Given a large enough sample set that may be true, but I don't think we are talking about anyone who has sat through a LOGO class in elementary school or hand coded some HTML. When I say "typical programmer" I mean someone within one or two standard deviation of the mean. I am also assuming that programming ability is a Poisson distribution. (I think I've just demonstrated everything I can remember from one semester of college statistics for technical majors.)

Now why do I express the law as a double negative implication? I blame the self-esteem driven coddling I received in public schooling. When you are failing you are "not succeeding" and when you are rebelling you are "not conforming." Everything is expressed as the desired outcome, and rather than using an antonym the desired word is used, with a negation in front of it. As a good Computer Science major I spent a few weeks in propositional logic. This math subset has a lot of theorems. And one of them is is Transposition, where we can swap the order of the implication as long as we negate both sides. Taking a desired statement, and adding two negations and still have it mean something is really cool.

This them begs the question, if a typical programmer is using a language feature or pattern correctly then it can be expressed as a fundamental part of a FizzBuzz solution then why don't I say it like that? The problem is we start with the hypothetical typical programmer. Next, we would need to see them use the language feature or pattern correctly. And only then can we conclude an integral FizzBuzz solution exists. I don't like this formulation because we start with end results looking backwards rather than a prediction looking forward. We would start with real programmers correctly using real technologies and concluding something arbitrary and contrived. But if we start with something arbitrary and contrived we can project onto real programmers and real situations without waiting for those real situations to occur. Much like concluding that a ball will hit the ground without dropping it. This is especially useful for technologies that have not seen widespread use. By starting with the technology we can then use this hypothesis as a lens to examine potential technologies a priori rather than as a post mortem.

The reason I came to formulating this hypothesis is because of the depths of thinking I went to while contemplating the various parts of this project. Part of that was reflecting on why it was a good interview question for testing programming skills. Not design, engineering, or architecture skills, but programming skills in the raw. It's a bit like the NFL combine. Before players are drafted by professional teams the teams get all of the top candidates together and test basic skills. How fast can you run, how much can you bench press. Let’s see you catch balls, see you throw balls, and see how smart are you. (No kidding, the NFL sits them down for an IQ test. And offensive tackles tend to be smarter than the quarterbacks, and way smarter than the wide receivers.)

But what makes FizzBuzz a good proxy for programming aptitude? FizzBuzz has three key aspects to most solutions: repetition, calculation, and decision. And these three aspects pretty much sums up most of what programming is about. There are some exceptions, and most of those interactions are hidden behind APIs calls. So if a programming language feature or pattern doesn't help you repeat, calculate, or decide, what good is it for programming?

For this entry I am going to use the FizzBuzz Hypothesis to answer a question that has bothered me since the Java programming language has been released: why is there both a short circuted and fully evaluated boolean operator in the Java Programming Language? And can you use both meaningfully?

public class FizzBuzz05 {

  static boolean lessThan100(int val) {
    return val < 100;
  }

  static boolean printNumber(int val) {
    System.out.print(val);
    return true;
  }

  static boolean maybeFizz(int val) {
    if (val % 3 == 0) {
      System.out.print("Fizz");
      return true;
    } else {
      return false;
    }
  }

  static boolean maybeBuzz(int val) {
    if (val % 5 == 0) {
      System.out.print("Buzz");
      return true;
    } else {
      return false;
    }
  }

  public static void main(String[] a) {
    int num = 0;
    while (lessThan100(num++) &&
      maybeFizz(num) | maybeBuzz(num) || printNumber(num)) {
      System.out.println();
    }
  }

}

Most of this solution is spent writing a method that makes the key point more readable. There are four different methods that do four separate things required by the solution. One method tests if the number is less than one hundred. One method prints out the number that is passed in. One method when passed in a number divisible by three prints out "Fizz." And there is one last helper method that does the same as the last, except testing for 5 and printing "Buzz." If the Fizz or Buzz methods print out a value, then they return true. If no value was printed, false is returned. None of these helper methods print a new line; that will be the task of the main method. You may be wondering where the helper method is that prints out "FizzBuzz". The reason we don’t need one is the point of this particular exercise.

The magic happens in line 33, a group of three of the helper methods joined by two different boolean or operators. The first one is the fully evaluated or "bitwise" version and the other is the short circuited or "logical" version. According to operator precedence we work with the bitwise operator and work that one first. When going through the loop both sides of this operator will be evaluated, regardless of the results. So in the cases where we have a number evenly divisible by three and five we will get both sides of the expression evaluated, even though we know after fizz that the result will be true.

The second operator, of lower precedence, is the logical or short circuit operator. If either of the methods on the left hand side had printed out their FizzBuzz value then we would be getting a true result on the left side. By short circuit rule we would not evaluate the right hand side and no value would have been printed. This is a good thing, because the method will always print a number regardless of what it is. If we print out Fizz or Buzz or FizzBuzz we are not supposed to print out a number at all. But if neither of the methods return true and no other values have been printed out then we need to print out the number. Since the left hand side is false in those cases the right hand side is evaluated. And as a side effect of the evaluation the number is printed.

So there, in one single expression, we have both a fully evaluated and short circuited logical or operator. It looks like typical developers can use this correctly.

No comments:

Post a Comment