Ow, my brains. (DRY)

At work I was faced with dumping out some reports of some stuff. No big deal, Ruby to the rescue. I have a huge array of lines of data and I need to select out a few bits and output them, doing some simple manipulation to it in the process. So I ended up with something like:

File.open('some_filename', 'w') do |f|
    f.write lines.sort{|a,b| a[0] <=> b[0]}.select{|a| a[0] < 50000}.collect{|a| a[1]}

But it turns out I need to do this multiple times. Each time I do it, it will have a different filename AND a different select criteria. Changing the filename is easy; just make an array of strings and iterate over it.

But how to change the select criteria? If it was something easy, say I always wanted all the data < X for various values of X, then it would be simple to code; just pass in X as a parameter. But say I want the data greater than X in one file and the data less than Y in another file? Or maybe a third report with values == Z. Or who knows what else. Now it's not just data varying; the logic varies also.

A few years ago I'd probably have copy/pasted all the code and just changed the little bit in the select block, like a goon, and rightly suffered for it. But Ruby lets you assign blocks to variables. Then you can send the block to an iterator using &block:

[ ['one_filename', lambda{|a| a[0] < 50000}], ['another_filename', lambda{|a| a[0] > 70000}]].each do |fn, test|
    File.open(fn, 'w') do |f|
        f.write lines.sort{|a,b| a[0] <=> b[0]}.select(&test).collect{|a| a[1]}

It's funny how the code is simultaneously shorter AND better AND more powerful than it would be copy/pasting or using some other method. I remember reading in a Lisp book that this is often a sign that the code is good. It's essentially the DRY principle at work.

This kind of programming is very "emotionally satisfying", but at the same time it's often very hard to wrap my head around. It took me a while to figure out that the above is what I wanted, and to realize that Ruby was capable of it.

I remember very well when I was just learning QBASIC 10+ years ago in high school, this is exactly the kind of thing I always WANTED to do. I have variables; let me use variables to vary EVERYTHING, data and logic and code, everything. I couldn't do it then, obviously. If only I'd have picked Lisp instead of BASIC. Now I feel like I'm kind of re-learning or un-learning all the limitations of the iterative, clunky ways of thinking of programming that were so ingrained by college and years of C/C++. (I remember a professor saying that function pointers in C++ are "scary things, and probably too advanced for this class to cover".)

February 01, 2007 @ 7:11 AM PST
Cateogory: Programming
Tags: Lisp, Ruby

1 Comment

Ciaran McCreesh
Quoth Ciaran McCreesh on February 03, 2007 @ 2:50 PM PST

Function pointers in C++ are indeed scary. This is why serious C++ programmers instead use callable types (or functors, or whatever you want to call them) and templates. All the power of Ruby blocks, and none of the type unsafety!

Speak your Mind

You can use Markdown in your comment.
Email/URL are optional. Email is only used for Gravatar.