DEV Community

Cover image for Hamming (coding challenge) Part II
Tunde Oretade
Tunde Oretade

Posted on

Hamming (coding challenge) Part II

Hamming coding challenge Part I

This article is the second part of Exercism's Hamming coding challenge. Initially I had explained my first solution and the lesson learnt as hinted by Exrecism. This second part focuses more on how I refactored my initial solution as seen in part I.

The refactored code goes like this:

class UnequalStrandError < ArgumentError def initialize(error_message = 'Strands must be of equal length') super end end class Hamming def self.compute(first_strand, second_strand) new(first_strand, second_strand).distance end attr_reader :distance, :first_strand, :second_strand private def initialize(first_strand, second_strand) @first_strand = first_strand @second_strand = second_strand validate @distance = nucleotides.count { |n1, n2| n1 != n2 } end def nucleotides(first_strand, second_strand) first_strand.chars.zip(second_strand.chars) end public def validate raise UnequalStrandError unless first_strand.length == second_strand.length end end 
Enter fullscreen mode Exit fullscreen mode

From this second iteration you can observed that it is now leaner and readable. However, what did do to make it leaner and readable?
As usual I will start with the UnequalStrandError class. In the new code I created a function called validate. This will take care of raising errors whenever any error is encountered in the code. Note that I now have a one line code:

 raise UnequalStrandError unless first_strand.length == second_strand.length 
Enter fullscreen mode Exit fullscreen mode

compared to what I had earlier to help handle raising errors:

 raise UnequalStrandError if first_strand.empty? && second_strand.size.positive? raise UnequalStrandError if first_strand.size.positive? && second_strand.empty? raise UnequalStrandError if first_strand.size > second_strand.size raise UnequalStrandError if first_strand.size < second_strand.size 
Enter fullscreen mode Exit fullscreen mode

Secondly I refactored compute as :

 def self.compute(first_strand, second_strand) new(first_strand, second_strand).distance end 
Enter fullscreen mode Exit fullscreen mode

The compute method in the above is a class method as indicated with the self prefix and also in compute body, I instatiated Hamming class and called distance on it.
The distance method is defined in initialize method:

 def initialize(first_strand, second_strand) @first_strand = first_strand @second_strand = second_strand validate **@distance = nucleotides.count { |n1, n2| n1 != n2 }** end 
Enter fullscreen mode Exit fullscreen mode

and it is defined as an instance variable which is then exposed using attr_reader method.

 attr_reader :distance, :first_strand, :second_strand 
Enter fullscreen mode Exit fullscreen mode

Note that @distance is a variable holding the result derived from nucleotides.count { |n1, n2| n1 != n2 }. Also the nucleotides method defined in the body of Hamming class handles the separation and zipping of first_strand and second_strand
In the initial code I had used the split() method to get an array from the string supplied but I found chars() to be effective as well. After creating the array I zipped both first_strand and second_strand array together. While using the zip() method this is what it does:

 first_strand = [G,A,G,C] second_strand = [C,A,T,C] first_strand.zip(second_strand) result = [[G,C],[A,A],[G,T],[C,C]] 
Enter fullscreen mode Exit fullscreen mode

After zipping I called the count() method on the zipped array and this method was able to get a count of items in the sub array that are not the same.

With this refactoring for readability, I learnt about self, and what self means in the context of this challenge. I understood that when self is used in a code, it refers to the class and in this case it is reffering to the class Hamming.
I also learnt a new trick: instantiating a class in a class method and calling a defined method in the class on it.

 def self.compute(first_strand, second_strand) new(first_strand, second_strand).distance end 
Enter fullscreen mode Exit fullscreen mode

When Hamming.compute is called, the new class instantiation will also call the method involved. This is a quick way of getting results instead of having to create new objects of class Hamming.
I also learned that, I can raise Errors in the initialize method to catch the errors thrown as quick as possible.

These are what I learned while solving the hamming coding challenge on Exercism. I will be solving more challenges on Exercism and do watch out for more articles on how I solved and refactored the challenges.

Top comments (0)