Sunday, April 10, 2016

Learnings along the way of completing the Roman Numerals Kata via Exercism

  • Roman Numerals Kata
    • Things that I applied or learnt along the journey that led to the final solution -
    • Approach the problem in small steps. I was solving this kata for the first time, I didn't know of any pattern whatsoever initially, hence took this problem, one step at a time and it eventually helped me get to solution. Often times one worries about the end solution or just the destination, without traveling the journey bit by bit.
      • I know you may think this approach(solving for one test at a time as done in this commit and this one) wouldn't give you the desired solution that'll satisfy all cases, but trust me it will help you eventually get there. That's why this approach is worth it.
    • Used the older hash syntax in ruby so that I could leverage keys as strings. With this I could do away with the to_s that I was using here.
    • Learnt that I don’t have to use a more higher order class like the Numeric class. Instead, I could use Fixnum. I also learnt about the hierarchy of these classes. Numeric is the parent of Integer which in turn is the parent of Fixnum.
    • Learnt that reverse_each is much faster than using reverse.each and made the relevant refactoring here
    • Refactoring related learnings * When I was looking at other people's solution like that of rwz's, I realized I can define my ROMAN_NUMERALS related hash in the reverse order like what was done here and thereby I wouldn't even need to use the reverse_each method, saving me an additional computation. * I learnt that I could further simplify my solution after referring to rwz's solution. The if...else related branches that I had introduced to handle the subtractive notation related to roman numerals can be dealt without using that amount of branching. I made the relevant changes wrt this learning in this commit. * Learnt how we could use each_with_object to do away with a temp variable from henrik's solution. Appropriate change was made in this commit. Below is how the code looks before and after using each_with_object

Before

  def to_roman
    num = ''
    number_to_convert = self

    ROMAN_NUMERALS.each do |roman_numeral, numeric_equivalent|
      next if numeric_equivalent > number_to_convert

      quotient, remainder = number_to_convert.divmod(numeric_equivalent)
      number_to_convert = remainder
      num << roman_numeral * quotient
    end

    num
  end

After

  def to_roman
    number_to_convert = self

    ROMAN_NUMERALS.each_with_object("") do |(roman_numeral, numeric_equivalent), roman_equivalent|
      next if numeric_equivalent > number_to_convert

      quotient, remainder = number_to_convert.divmod(numeric_equivalent)
      number_to_convert = remainder
      roman_equivalent << roman_numeral * quotient
    end
  end
  

I don't want to write code for special cases until I understand the general case first.

 For me, this was something I wasn't doing very consciously all this while but I felt, it's definitely something              worth reflecting upon and further experimenting with.
  • Wrapping up, I would like to thank Jim for the above thought and for his work that has helped so many people. Also, my learnings here wouldn't have been possible without those who've contributed to exercism and to those whose solution I had an opportunity to refer to. I'm thankful to them as well.

Sunday, April 3, 2016

Learnings from solving the Hamming exercise on Exercism

Hamming Exercise

  • Learnt how to use count with each_with_index
    • This helped me do away with the temp variable distance that I had used as a counter.

Before - Uses a temp variable called distance as a counter

class Hamming
  VERSION = 1

  def self.compute(dna_strand_1, dna_strand_2)
    raise ArgumentError if dna_strand_1.length != dna_strand_2.length
    distance = 0
    dna_strand_1.chars.each_with_index do |strand_1_char, index|
      distance += 1 unless strand_1_char == dna_strand_2[index]
    end
    distance
  end
end

After - Uses count insead of distance . Taken from rwz's solution.

class Hamming
  VERSION = 1

  def self.compute(dna_strand_1, dna_strand_2)
    raise ArgumentError if dna_strand_1.length != dna_strand_2.length

    dna_strand_1.chars.each_with_index.count do |strand_1_char, index|
       strand_1_char != dna_strand_2[index]
    end
  end
end
  • Looking back at this new approach I could relate better to the Nitpick made in my solution by rikki - the friendly neighborhood code review robot, who said -

Whenever you are looping through a collection and find yourself writing a conditional (if or unless) nested inside of the loop, take a moment to look through the available enumerable methods. There are some very handy ones that might let you simplify.

Misc Learnings

  • Use chars to get an array of characters from a string
  • Use zip to merge corresponding elements of two or more arrays as used wrt henrik's solution. Sample usage -
2.3.0 :008 > b = [7, 9]
2.3.0 :009 > c = [4, 8]
2.3.0 :010 > a = [2, 3]
 => [2, 3]
2.3.0 :011 > a.zip(b)
 => [[2, 7], [3, 9]]
2.3.0 :012 > a.zip(b,c)
 => [[2, 7, 4], [3, 9, 8]]
2.3.0 :013 >
  • Learnt that one needn't have to use a class always(using a class was the first thing that came to my mind when solving this). A module could also be used to solve this. The reasoning behind this(mentioned below) is something I hadn't thought of earlier. It also kinda makes sense to me becuase with Modules one cannot create instances of it and if there isn't a need to create instances, then why not consider using a module. People may want to debate further on this(i.e., on using classes vs modules - that's beyond the scope of this post) but the below point in itself was something worth reflecting upon for me.

    Yeah, there's not much reason to use a class for this, since there are no instances involved over here.

  • Something similar was also mentioned in markijbema's solution.

Lastly, my learnings here wouldn't have been possible without those who've contributed to exercism and to those whose solution I had an opportunity to refer to. I'm thankful to them.