jueves, 15 de mayo de 2014

Languages Comparison 1

Recently I faced a simple problem on my everyday life about getting the different possible combinations from a given collection of items. Since it wasn't a small collection I thought about making a simple program to make the computation. As a .NET developer I chose C# for the job, once finished I thought it had many lines for such a simple problem. That gave me the idea of writing down the same algorithm in dynamic languages just to see how many lines were needed in that kind of languages and of course, for fun.

The chosen languages were Python and Ruby, and below is the code for each language:

C#

static void Main(string[] args)
        {
            var values = new List<string> { "A", "B", "C" };
            var combinations = Combinations(values);
            combinations.ForEach(c => Console.WriteLine(string.Concat(c)));
            Console.ReadKey();
        }

        public static List<List<string>> Combinations(List<string> values)
        {
            var comparer = new ListComparer<string>();
            var combinations = new List<List<string>>();
            foreach (var item in values)
            {
                for (int i = 0; i < (values.Count + 1); i++)
                {
                    var currentCombination = new List<string> ();
                    if (i < values.Count)
                    {
                        values.Take(i).ToList().ForEach(e => currentCombination.Add(e));

                        if (!currentCombination.Contains(item))
                            currentCombination.Add(item);
                    }
                    else
                        values.GetRange(values.IndexOf(item), i - values.IndexOf(item)).ForEach(e => currentCombination.Add(e));
                    if (!combinations.Contains(currentCombination, comparer))
                        combinations.Add(currentCombination);
                }
            }
            return combinations;
        }
    }

    class ListComparer<T> : IEqualityComparer<List<T>>
    {
        public bool Equals(List<T> x, List<T> y)
        {
            if (x.Count != y.Count)
                return false;
            var result = x.TrueForAll(item => y.Contains(item));
            return result;
        }

        public int GetHashCode(List<T> obj)
        {
            return obj.GetHashCode();
        }

    }

Python

def calculate_combinations(values):
    combinations = []
    for item in values:
        for i in range(0, len(values) + 1):
            current_combination = []
            if i < len(values):
                map(lambda e: current_combination.append(e), values[:i])
               
                if item not in current_combination:
                    current_combination.append(item)
            else:
                map(lambda e: current_combination.append(e), values[values.index(item):i])  
            if current_combination not in combinations and len(current_combination) > 0:
                combinations.append(current_combination)
    return combinations

values = ["a","b","c"]
combs = calculate_combinations(values)
for c in sorted(sorted(combs, key = lambda y: y[0]), key = lambda x: len(x)):

    print c


Ruby

def calculate_combinations(values)
  combinations = []
  values.each do |item|
    (values.count + 1).times do |i|
      current_combination = []
      if i < values.count
        values.take(i).map { |e| current_combination << e}
       
        if not current_combination.include? item
          current_combination << item
        end
      else
        values[(values.index item)..i].map { |e| current_combination << e }
      end
      if not combinations.include? current_combination
        combinations << current_combination
      end
    end
  end
  return combinations
end

values = ['a','b','c']

combs = calculate_combinations values
combs.each do |c|
  puts c
  puts '------------------'

end


Results:

  • C# sure was larger than the programs writen in dynamic languages, and even needed coding a class for lists comparison, that's a con. On the other hand, due to my previous experience, I had little to none error, but I'm sure even people without much C# experience could manage to easily fix their errors thanks to the (most of the time) helpful compile and runtime error messages, that's a pro.
  • Python was really nice and ended up being a compact program. Liked its approach for taking a subset from a collection and the fact that didn't need anything special for comparing lists. The only thing that annoys me a bit is the way you declare a lambda expression, using a "lambda" key word seems logic but I would have expected something shorter (like => is). I had a few of errors (discovered at runtime of course) but they were reasonably easy to fix due to good error messages.
  • Ruby also was able to naturally compare two lists and has really interesting syntactic sugar, like the optional use of parenthesis when passing parameters, looping syntax, how you add elements to a list, really simple lambda syntax and so. Aside from the previously mentioned features, I found a couple of thing that annoyed me, first, the "end" key word, they need to get rid of it, adds unecessary code lines and makes me feel like writing Pascal, Visual Basic, SQL, etc. not a good feeling when working on an OOP language. Also, the fact that return statement needed to be at the end of the whole method wasn't nice too. And finally, error messages I got were rather cryptic and had a hard time fixing small mistakes.

Conclusions:

Dynamic languages look really promising, specially Python (a mix of Pyhton and Ruby would be great), but when professionally working on large projects, I still can't see myself using a language where tracking errors is not a reasonably easy task, or where you need to run every execution path in order to find simple typos. So for now, I'll stick to C# which is a really nice language compared with other static languages.

The algorithm coded might not be really optimal and I guess Python and Ruby might have more elegant ways for solving that problem (so if a Python/Ruby developer wants to make any suggestion, you're welcome to do so). For the next post, I could try to solve the same problem (or a different one) but now using a functional language, like F#, in order to discover the virtues of functional programming. Thanks for reading, see you on next post.