Categories
Technology

Learn By Do. Part III

This is kinda like a bumper edition, since it wasn’t intended but after hacking this for a bit, it turned out to be a pretty useful exercise, no matter the language. Why? Primarily since you get to, depending on implementation, write your own data structures and use recursion. This combination of tasks usually allows you to explore and discover some interesting language-related issues. So then, without further ado.

The task: generate all the possible word combinations from a telephone number. The first stab, ignore dictionaries and words or phrases that actually make sense in English, and just be able to “translate” the number 23 => ad, ae, af, bd, be, bf, cd, ce and cf. If the number is 233, the options become add, ade, adf, aed, aee, aef and so on all the way through to cff. And then later maybe we can reverse the process and make up a word to come up with a number?

Right, the strategy: write tests first! The first thing i need is a class that can accept and validate a string as being a legit phone number.

class TestPhoneNumber < Test::Unit::TestCase
  def test_ctor
    p = PhoneNumber.new("0215556767")
  end
  def test_cant_have_alpha
    assert_raise(ArgumentError) {PhoneNumber.new("065a223")}
  end
  def test_cant_have_punctuation
    assert_raise(ArgumentError) {PhoneNumber.new("087!322")}
  end
  def test_cant_have_other
    assert_raise(ArgumentError) {PhoneNumber.new("0987%")}
  end
  def test_split_numbers
    p = PhoneNumber.new("0123")
    assert_equal(["0", "1", "2", "3"], p.split_numbers)
  end
end

I also wanted to store the individual digits since in my mind’s eye, i can see myself using a map to index and reference the letters using the digits so it just feels like something i need to do right now. We’ll see how it plays out. Turns out, the implementation is pretty straightforward:

def initialize(number_as_string)
   if number_as_string =~ /\D/
     raise ArgumentError.new("Can only contain numbers")
   end
   @split_numbers = split_the_numbers(number_as_string)
end

A simple regular expression takes care of most of what i need to cover. Splitting the numbers is a simple loop, no fuss there.

def split_the_numbers(numbers)
  i = 0
  result = [""]
  while i< numbers.length
    result[i] = numbers[i,1]; i += 1
  end
  return result
end

The trick now is to be able to write something like:

PhoneNumber.new("23").possible_words.each do {|word|
 print word
}

And get the output:

ad
ae
af
bd
be
bf
cd
ce
cf

Mmmm… Tree? Graph? Simple arrays and hashes? What will it be? (…to be continued)