Programming Praxis – Learn A New Language

The goal in today’s Programming Praxis exercise is to solve a previous exercise in a language you’re not proficient in. Since I’m a big fan of statically typed languages, I figured I’d try a dynamically typed one today, the two most obvious options being Python and Ruby. I’ve done a little Python coding (among others, it’s the scripting language for the text editor I use). Of the two, Ruby has always seemed the more interesting one (partly because it embraces functional programming more than Python does), but I haven’t yet had occasion to use it. Let’s remedy that.

As for the exercise, I picked Steve Yegge’s Phone-Screen Coding Exercises, figuring seven different small problems might cover slightly more ground than one bigger one.

Reversing a string works just like in Haskell, save for the fact that we have to explicitly convert the string to a character array.

def reverse(s)
    s.chars.inject {|rev, x| x + rev }
end

For calculating Fibonacci numbers we can’t use the typical Haskell solution of zipping an infinite sequence with itself, so instead we just use a fold, keeping a buffer of the last two values calculated.

def fib(n)
    (2..n).inject([0,1]) {|fs, i| [fs[1], fs[0] + fs[1]]} [[1, n].min]
end

For the 12 times table, the solution is basically the same as the Haskell one, though since Ruby has no list comprehensions we have to use a nested map.

def timestable
    (1..12).map {|r| puts (1..12).map {|c| "%4d" % (r * c)}.join}
end

Summing numbers from a file is straightforward: read the lines of the file, convert them to numbers and sum them up, same as in Haskell.

def sum_from_file(file)
    File.open(file) {|f| f.readlines.map(&:to_i).inject(:+)}
end

Printing the odd numbers from 1 to 99 is trivial as well.

def odd_numbers
    p (1..99).find_all(&:odd?)
end

Getting the highest integer in a list can be done with a simple fold.

def maximum(array)
    array.inject {|max, i| i > max ? i : max}
end

Again, identical to the Haskell version, if a bit longer due to the lack of partial application.

def to_rgb(r, g, b)
    sprintf("%02x%02x%02x", r, g, b)
end

As usual, our tests:

puts reverse("Hello, World!")
p (0..10).map {|x| fib x}
timestable
puts sum_from_file("numbers.txt")
odd_numbers
puts maximum([-1,3,2])
puts to_rgb(255, 128, 65)

Naturally, a test this short is hardly conclusive evidence, but so far Ruby doesn’t disappoint. All seven functions are one-liners, as you would expect from any competent language, though this is slightly marred by the need for end keywords. I prefer the Haskell and Python approach in this, since it eliminates these useless almost-empty lines. On the plus side, first-class functions and all the common enumerable methods mean you can often directly transcribe the Haskell solution, which is nice. It won’t be replacing Haskell as my favorite language any time soon, but out of the more mainstream languages you could do a lot worse than Ruby.

Tags: , , , , , , ,

Leave a comment