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 lambdaconstruct 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.
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
string
doesn't appear to implement a generic sequence type (Enumerable
in Ruby) in 1.9.3; hence the str.split("")
to convert the string into an Array
. I don't quite understand the logic there; my personal favorite language C# makes sure that the string type implements IEnumerable<char>
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
reduce(:<<)
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.