Skip to content

Anything you can DO I can DO better

December 5, 2009
tags: ,

Here is a simple world count in Ruby from recent post at Refactormycode.com:

s = "The green hat is the twin of the green hat"
h = Hash.new(0)
s.split.each do |w|
  h[w.downcase] += 1
end

If you know Ruby then you know this isn’t very idiomatic. Here is something closer to would normally see in Ruby world:

h = s.downcase.split.inject(Hash.new(0)) { |h,k| h[k] += 1; h }

But the style of the original code is something I’ve seen often in Perl:

my $s = "The green hat is tHe twin of the green hat";
my %h; 
for my $w (split /\s+/, $s) {
    $h{ lc $w }++;
}

Now if brevity was key to better code then you could do this:

my %h; $h{$_}++  for split /\s+/, lc $s;

But I think its better to do something like this:

my %words = do { 
    my %h;
    $h{$_}++  for split /\s+/, lc $s;
    %h;
};

OK this is not a great example of do blocks and isn’t even as DRY compared to original code. But I do find this more aesthetic IMHO.

Perhaps a better example of what do blocks can do is to produce another naff example!

my $num_string = do {
    my $num = get_random_num_between_1_and_3();
    if    ($num == 1) { 'one' }
    elsif ($num == 2) { 'two' }
    elsif ($num == 3) { 'three' }
};

do blocks return the last evaluated result. And result can be plural:

my $year = get_year();
my ($champions, $runners_up) = do {
    if    ($year == 2003) { ('England', 'Australia')    }    
    elsif ($year == 2007) { ('South Africa', 'England') }
};

say "Rugby World champions for $year: $champions  Runners up: $runners_up";

So do blocks are useful tools in helping to make code more focused and avoid unnecessary pollution of variables into calling namespace.

But we can either move up another notch by using anonymous subroutines:

my ($champions, $runners_up) = sub {
    given ($_[0]) {
        when (2003) { return ('England', 'Australia')    }
        when (2007) { return ('South Africa', 'England') }
    }
}->( get_year() );

This anon sub block is built and then immediately executed by the ->() bit.

So works much like a do block except we can short circuit flow with return thus enabling us more freedom and control over what these blocks of code can do.

Using do / anon sub blocks is something that Perl can definitely learn from Ruby. Here is another recent example of do usage that I like a lot (see has).

NB. Another advantage of the do / anon sub block approach is that at anytime the block gets big or its needed elsewhere then it doesn’t take much to turn it into a proper subroutine.

 

I’ll end with my obligatory autobox solution of the original code:

my $s = "The green hat is tHe twin of the green hat";

my %words = $s->lc->split( qr{\s+} )->hash_count->flatten;

sub autobox::Core::ARRAY::hash_count {
    my $list = shift;
    my %h;
    $h{$_}++ for @$list;
    \%h;
}

You can even do:

'The green hat is the twin of the green hat'->lc->split( qr{\s+} )->hash_count->flatten;

But I can’t think where something like this would ever be useful in the real world? Though it is nice to look at when running it via the extremely useful Devel::REPL 😉

/I3az/

Leave a comment