Exercism № 1: Hamming

March 17, 2019 , posted under ruby crystal exercism
Exercism № 1: Hamming Image Credit: unsplash-logoPhilip Estrada

Counting Similarities and Differences

The first real Ruby/Crystal Exercism problem is called “Hamming”, and putatively refers to Hamming codes, but really just involves counting differences in two strings. One can iterate over the strings a number of different ways. This is one of the more elegant:

module Hamming
  extend self
  def compute(strand_1, strand_2)
    raise ArgumentError.new unless strand_1.size == strand_2.size
    strand_1.each_char.zip(strand_2.each_char).count do |(first, second)|
      first != second

This is also valid Crystal code. We use a “guard clause” to check whether the arguments are of equal length (using unless because anything Ruby-specific must be *The Ruby Way*™), and using each_char because unlike chars, it does not create a new array (just an Enumerator). zip gives us a new array which associates the elements of the input arrays at each index, which these languages will destructure for us in the block signature for count, and the resulting block body is simplicity itself. As mentioned, there are a number of alternatives, most of which are written identically in both languages.


Now, you might be asking yourself, “If there is more than one way to do it, which way is fastest?” Whenever you start to think this, stop. 97% of the time, this is a bad idea. Your first priority is to write clear and understandable code. When performance cannot be ignored, get a performance profile before you start tweaking. Keep in mind, benchmark results can vary depending on everything from the order the benchmarks are run in to the phase of the moon. In most cases you will get less than an order of magnitude from writing a method one way or another, whereas a more clever algorithm or heuristic has greater potential for order-of-magnitude improvements.

# (...)
def compute(first, second)
  raise ArgumentError.new unless first.size == second.size
  (0...first.length).count { |idx| first[idx] != second[idx] }

We skip creating an intermediate array, and instead create a Range, which just stores the first and last elements, and whether the range is exclusive. Once again, there isn’t a real alternative to count. “But wait,” say you, “This method doesn’t transform the strings at all! Shouldn’t that be faster?” That’s exactly the kind of thinking that gets you into trouble! The difference between the two is insignificant. Code doesn’t become faster just because you use an index variable. More than likely, the elegant path will also be the fastest. Let’s take a look at one more solution:


This is an interesting approach, but it’s somewhat slow, and probably counterintuitive. I would probably not recommend actually using this technique in real-world code.


def compute(*strands)
    .count { |arr| arr.all?(&arr.first.method(:==)) }
rescue IndexError
  raise ArgumentError


def compute(*strands)
    .count { |arr| !arr.all?(arr.first) }
rescue IndexError
  raise ArgumentError.new

Whether or not it makes sense for this code to take an arbitrary number of arguments is beyond the scope of this problem, but it’s interesting to look at how this may be generalized. Ruby and Crystal both use the splat * operator to allow a method to receive an arbitrary number of arguments, but in Crystal this produces a Tuple rather than an Array, thus forcing us to use to_a to get access to Array#transpose. Both languages allow the def and end of the method body to be used as the begin and end of the rescue statement, but Crystal requires an Error instance rather than just an error class name.

Problem Design

The object being required here is not very useful. Either this compute method is intended to be called on exactly two objects, in which case it should probably be implemented as String#hamming_code, or it should probably be some sort of mix-in extension (this is much more easily accomplished in Ruby).

comments powered by Disqus