среда, 30 июня 2010 г.

Phone digits to letters

Today on a russian programming board I found small etude - write a program in your favorite language, which accepts phone number with digits 2-9 as a string and prints all possible ways to write it as letters (order doesn't matter). So, for example, for input "23" the program should print AD, AE, AF, BD, BE, BF, CD, CE, CF.
I wondered how Fantom solution would look like? Here's the result:

Str[] expand(Str num)
{
num.isEmpty ? [""] :
expand(num[1..-1]).map |s|
{
table[num[0]].map |c| { c.toChar + s }
}.flatten
}
private static const Int:Int[] table := [
'2':['A','B', 'C'], '3':['D','E','F'],
'4':['G', 'H', 'I'], '5':['J','K','L'],
'6':['M', 'N', 'O'], '7':['P','Q','R', 'S'],
'8':['T','U','V'], '9':['W','X','Y','Z']]

Not very efficient, but quite compact solution. Method expand accepts phone number and returns list of all possible letter representations. So, for empty input it returns just list with one element - empty string.
If input is not empty, it does the following:
  1. calls itself to get the result of input without first digit
  2. gets all possible letters for first digit
  3. adds each letter in the beginning of each word from #1
  4. returns resulting list
One thing I don't like here is table declaration. What can we do here? First, it is possible to convert Map to List, it will slightly decrease readability of expand method, but we can easily calculate list index for a given digit char just by subtracting '2' from it, so table[num[0]] will become table[num[0]-'2'].
Another possible improvement - use strings for letters instead of lists, so ['A','B', 'C'] becomes ["ABC"]. But here's a small problem with Str - it does not have neither Int[] toChars() nor ,Obj?[]? map(|Int, Int->Obj?|) slot, so we'll have to create a temporary list inside first map, i.e. instead of this:
  table[num[0]].map |c| { c.toChar + s }
there would be something like this:
tmp := [,]
table[num[0]].each |c| { result.add(c.toChar + s) }
return tmp
So, in order to simplify declaration, it is easy to implement method like this:
static Int[] chars(Str s)
{
result := Int[,]
s.each { result.add(it) }
return result
}

So, declaration can be simplified in obvious way - instead of ['A','B','C'] will be chars("ABC").