Tonights Meet and Code dinner was a huge success. We must have had 25+ people show up! I wish I had taken a head count, but tonight was definitely the first time that we have had to bring in extra chairs. The night started off with Harper Trow giving an excellent overview of the Ruby ecosystem. Then I stepped up and gave a quick overview of some basic Ruby concepts and syntax. Next Kevin Hazzard gave an awesome presentation on using IronPython and C# to call WCF SOAP web services and dynamically generate proxies that can then be used to call the web services! Pretty sweet no matter how many times I see it! Then last I got up again and gave a quick look at some of the code that I had used at my Function Programming Features in C# 3.0 talk and then translated it into Ruby to show how much less Ruby code it takes to get the same effect.
The C# code looked like this:
var domains = new List<string> { "www.google.com", "www.yahoo.com", "www.akamai.com", "www.ask.com" }; int pingCount = 20; IEnumerable<IEnumerable<int>> totals = domains.Select(d => d.Repeat(domain => new FakePing() .Send(domain).RoundtripTime, pingCount) .Where(p => p != 0) ); IEnumerable<string> averages = totals.Select(p => (p.Sum() / p.Count()).ToString() ); domains.Zip(averages).ForEach(p => Console.WriteLine(p[0] + ": " + p[1]));
With the helper methods looking like this:
public static IEnumerable<TResult> Repeat<TArg, TResult> (this TArg val, Func<TArg, TResult> func, int times) { for (int i = 0; i < times; i++) { yield return func(val); } } public static T Fold<T>( this IEnumerable<T> list, Func<T, T, T> func) { return Fold(list, func, default(T)); } public static TResult Fold<TArg, TResult>( this IEnumerable<TArg> list, Func<TResult, TArg, TResult> func, TResult result) { foreach (TArg item in list) { result = func(result, item); } return result; } public static IEnumerable<T[]> Zip<T>(this IEnumerable<T> list1, IEnumerable<T> list2) { var enumerators = new[] { list1.GetEnumerator(), list2.GetEnumerator() }; while (true) { int current = 0; var result = new T[enumerators.Length]; foreach (var enumerator in enumerators) { if (!enumerator.MoveNext()) yield break; result[current++] = enumerator.Current; } yield return result; } } public static void ForEach<TArg>( this IEnumerable<TArg> list, Action<TArg> action) { foreach (TArg item in list) { action(item); } }
The Ruby looked like this:
require 'FakePing' require 'UtilityMethods' ping = FakePing.new domains = ['www.google.com','www.yahoo.com','www.akamai.com','www.ask.com'] result_groups = domains.map do |domain| 10.repeat do ping.send(domain).roundtrip_time end end result_groups.map! do |group| group.select { |reply| reply != 0 } end averages = result_groups.map do |item| sum = item.inject(0) { |sum,ping_time| sum += ping_time } sum / item.length end (domains.zip(averages)).each do |z| puts "#{z[0]}: #{z[1]}" end
With the helper method looking like this:
def repeat(&repeatable) result = [] self.times do result << repeatable.call end result end
Now most of that is library code which you would write once and not look at for years. And I’m certainly not switching from C# as my main development language, but it sure is nice to play around with this stuff and see things from a little bit different angle! It also amazes me how far C# 3.0 has taken us from what was possible in C# 2.0. Even looking at the code above, it is crazy thinking what it would have looked like if it was rewritten in C# 2.0.
Loved the article? Hated it? Didn’t even read it?
We’d love to hear from you.