среда, 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").


понедельник, 22 февраля 2010 г.

Productivity

I believe everyone has his own favorite work conditions. Some people like working from home, when they can go to the kitchen and boil a cup of coffee, someone prefers offices.

About three weeks ago I found my favorite workplace - I go to a restaurant near our office building, take my laptop, order some food/beer/coffee, and don't turn on Wi-Fi. So, imagine - you are in restaurant alone, no internet, no friends, so nothing disturbs you. At the same time, you can smoke, drink, and eat without going to somewhere else. My laptop provides about 4-5 hours of active work, during this time I drink about 4 pints of beer and make the same amount of work as during two days in office approximately. I do this about 1-2 times a week and very satisfied so far.

However, I'm sure that I can't do this every day, not because of beer, but because now when I go to this cafe alone, I tune myself on some sort of coding marathon.

четверг, 15 октября 2009 г.

Kiddy task on Fan

I think that the expressive power of programming language can be easily felt on very simple programs. For example, on one of programming forums someone asked for advice how to convert strings in format "(N[-M],)*" where N and M are integers to list of ints (exactly what you see in any print dialog in print pages text field, something like 5-10,8,13,17).
I wonder that there are people who ask such things on forums, but anyway, let's see the solution of this kiddy task on Fan:

result := Int[,]
"5-10,8, 13"
.split(',', true).each |s|

{
r := s.split('-').map {it.toInt}
result.addAll((r.first..r.last).toList)
}
echo(result.unique.sort)



So, what is going on here?
First, we declare result as an empty list of integers.
Then we take an input string and say that we want to split it by ',' character and remove trailing whitespaces for each string
Then, for each string we do the following:
Split string by '-' char and convert resulting Strig list to list of Integers.
(r.first..r.last) is a literal for range type.
What's interesting here is that inner part produces correct result whether it processes string N-M or just N. Because when there is no '-', split method returns list with one element. And in this case r.first == r.last and we get range with only one int.

And, finally, print the result with removing duplicates and sorting