Adding to the swarm of developer blogs since 2007

Tuesday, March 13, 2012


The Pragmatic Programmer is a great resource for any developer; on the subject of learning new programming languages it states:
Learn at least one new language every year. Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut.
Generally I try to follow this advice as best I can (although the real world can often put a damper on such things). Recently at work I've had the pleasure of getting to learn Ruby (and by extension, Rails) and I thought I'd share some brief thoughts on the language. Some background might be in order; I already know Python and use it quite regularly so I have some preconceived notions of what a dynamic scripting-type of language should be. That said I've thoroughly enjoyed what I've seen of Ruby so far. Some things I've noticed:
  • Strong OO constructs: I love that Ruby embraces OO and things like encapsulation on a first-class level (without all the _nonsense_). The pythonic way of doing OO always annoyed me. The ability to monkey patch a class is neat but I'll have to spend a lot more time with it to see a great use beyond occasional bug fixes.
  • Truly functional: Actual first-class functions in a language--impossible you say! I am finding more and more that I want to be able to pass and store functions and Ruby makes it a breeze. Python's sad and limited
    construct is woefully inadequate in far too many scenarios (I understand why they limit it however it still cripples the language).
  • Documentation leaves something to be desired: Don't get me wrong, there are a ton of great Ruby resources, however when you get to the meat and potatoes of how some of the functions work I've had a hard time understanding some of the documentation that's out there.
I read a blog recently that mentioned a quick exercise for a programmer new to a language--write a run-length encoder for simple text-only strings. Figuring this was pretty simple I wrote up the first thing that came to mind in a little function:

def rle(str) token = nil counter = 1 out = str.split("").reduce("") do |cur, obj| if obj == token counter += 1 cur else if token != nil out = cur + counter.to_s + token else out = cur end token = obj counter = 1 out end end if token != nil out += counter.to_s + token end out end

This is the standard imperative way to do things, and the most obvious for many programmers. Something interesting is that Ruby's
doesn't appear to implement a generic sequence type (
in Ruby) in 1.9.3; hence the
to convert the string into an
. I don't quite understand the logic there; my personal favorite language C# makes sure that the string type implements
so you can do some fancy stuff with LINQ. The mutability of strings in Ruby may play a role here but I haven't explored it fully.

I'm pretty unhappy with something that's so verbose and filled with plumbing. So I ran off and wrote a new run-length encoder that's a bit more terse:

def rle2(str) processed = str.split("").chunk {|l| l}.reduce(:<<) if processed.count > 0 output = processed[1].count.to_s + processed[0] end output += processed[2..-1].reduce("") {|out, item| out += item[1].count.to_s + item.first } end

I was pretty happy with this solution however you'll notice that it could be quite a bit shorter if I didn't have to special-case the first chunk. The problem is the
is appending to a list with the first element, causing it to be flatted into the array. The solution is to add an initial empty list to the reduce call so the first item won't be hijacked. We can now also get rid of the two-pass reduce and get this nice little function:

def rle2(str) str.split("").chunk {|l| l}.reduce("") {|out, item| out += item[1].count.to_s + item.first } end

All in all a good time, I hope to be getting really dirty with Ruby in the future but I thought I'd share some thoughts about my first experience.