A funny thing happened to me on Friday. I was scheduled to present at one of the Strange Passions events for the first Strange Loop conference. Earlier in the morning, I practiced a code kata I was planning on showing when it was my turn to present. The kata was an exercise I use when I do tech interviews of prospective candidates for developer positions at the consulting firm where I work. It calls for building a calculator that can parse a string expression and return the result of the expression as a string. This is an exercise that I have used for years, one that I have done many times in Java as well as in Ruby. The problem is ideal for observing test-first habits in programmers because it is a very easy problem for which to write tests.
When the time came for me to present, I gave a few minutes of introduction to the exercise and dove in. The first test took less than a minute to write, and I switched over to writing just enough to pass the first test. And then I froze. My mind went completely blank. This is an exercise that can be solved many different ways, and at that moment, in front of a decent crowd, I couldn't think of a single one. It was a totally unnerving experience. So I sat there for a minute or two, struggling to remember how I had solved this before. The audience grew impatient. I can only imagine that it was like watching a train wreck. Then, after what felt like an eternity, something unclogged in my brain and I started up again.
The next day, I thought a lot about it. I remembered that something similar happened to me on the night of my wedding. My wife and I took Tango lessons for 4 months prior to our wedding because we wanted our first dance to be a Tango. We had a routine that the dance instructor helped us work out and we practiced and practiced. On the night of the reception, the music started and I began the routine. Then, mid-way through, everything went blank. It was a two-part routine, and the lights went out in my head at the end of the first part. I panicked and led my wife through the first part of the moves we had practiced.
These lapses in memory to execute something that is almost second nature while in front of a crowd is generally thought of as stage fright. It may very well be that I have sporadic bouts of this phobia, and what happened Friday at Strange Loop was just another occurrence. I teach classes and had presented the day before at the conference, so it is not unusual for me to be presenting something in front of an audience. It was, however, disappointing to leave the crowd twiddling their thumbs while the wheels in my head spun for traction and I am unsure what to do to prevent this same episode from happening again.
I have written several variations of the calculator kata, that I present here to anyone interested in picking this up as an exercise for their own practice. The exercise is as follows: create a simple calculator that accepts a String arithmetic expression and returns the result of the expression, also as a String.
The Spec
Solution 1
This is the ugliest of the bunch. Split the expression at each space character, then add the parts of the expression into an array until there are 3 elements in the array, at which time I figure out which operator should be applied to the left and right sides of the expression, and then calculate and store the result as the left side of the expression to be evaluated next. On line 7, I am also adding a convenience method to the array to reduce some code duplication. This solution is readable but wordy.
Solution 2
This next approach greatly simplifies the strategy for calculating a binary expression. I still split on spaces and add each part of the expression string to an array, but instead of figuring out the operator for the expression, I take advantage of the operators existing as methods on the Fixnum class. This solution turns the left and right operands into integers, passing the right operand and the operator to the 'send' method of the left operand integer, and storing the result of the evaluation back in the parts array.
Solution 3
This one is a variation on Solution 2. Instead of splitting on spaces, I use a regular expression and capture groups to extract out the operands and operators for each part of the expression contained in the string. This solution still takes advantage of arithmetic operations existing as methods on the Fixnum class, so the difference from Solution 2 is primarily in the way that the parts of the expression are extracted.
Solution 4 -- Updated - 10/27/09
This final solution is defers to the Ruby interpreter to solve components of the equation. This approach can't simply rely on the Ruby interpreter to evaluate the entire expression because the arithmetic operators have different precedence order in Ruby. I use capture groups and positive lookahead (?=(\d+))
to get the three parts of the binary expression, thereby ensuring left-to-right expression evaluation (which is how I wanted the Calculator to work).
Special thanks to Michael Easter for pointing out the flaw in the original version of solution 4, which actually did just hand the expression over to the interpreter. I hadn't included a test that combined all four operators, which exposes the order precedence issue in Ruby. That has been remedied in the spec above.