Dougal Campbell's geek ramblings

WordPress, web development, and world domination.

Perl geekery: building hashes

Say we’re writing a program in Perl, and we need to pass a lot of data back and forth between subroutines. Using global variables is bad practice, and we often use the slightly-less-bad method of passing around a big hash variable. But it’s a pain to always use the values in the hash, so a lot of our code uses individual scalar variables, and stick them into (and pull them out of) the hash as needed. But when you have a lot of values to move around, it’s a pain in the neck. You don’t want a big, ugly block of code like this:


#...assume that we declared %hash, 
#   $foo, $bar, $baz, etc previously...
$hash{'foo'} = $foo;
$hash{'bar'} = $bar;
$hash{'baz'} = $baz;
# ...ad nauseum...

Surely, there’s a more elegant way to do this, right? Of course there is. The mantra of perl programmers is TMTOWTDI (There’s More Than One Way To Do It)! The first and most obvious approach that comes to mind is to use “variable variables”, technically known as “symbolic references”:


my %hash;
my @vars = qw(foo bar baz);
foreach my $var (@vars) {
  # This uses a symbolic reference. When $var is 'foo',
  # then saying $$var is like saying $foo.
  $hash{$var} = $$var;
}

The problem is that symbolic references are frowned upon, and will cause perl to get angry with you if you’re running with use strict (as you should be). You can get around this by declaring the scope of $foo and friends as ‘our’ instead of ‘my’, and by using no strict 'refs' inside the foreach loop. I’m pretty sure that this wouldn’t cause any memory leaks, but it’s still an iffy solution because you have to change the scope of a bunch of your variables to ‘our’, which might have undesired side-effects.

The next refactoring is a nice improvement. The main difference is that it requires you to stick your list of values into a temporary array. Just keep in mind that this array will be modified (emptied) in the process:


my %hash;
my @keys = qw(foo bar baz);
my @values = ($foo, $bar, $baz);
foreach my $key (@keys) {
  $hash{$key} = shift @values;
}

Ah, much better! No problems running strict, and not too ugly. A final improvement, suggested to me by David Narayan, is to use a hash slice:


my %hash;
my @keys = qw(foo bar baz);
my @values = ($foo, $bar, $baz);
@hash{@keys} = @values;

That’s about as succinct as it’s going to get. The only disadvantage here might be if your list of key/value pairs was large, this would probably use a lot of memory. In that case, you’d probably want to use the foreach loop, as in the previous example.

About Dougal Campbell

Dougal is a web developer, and a "Developer Emeritus" for the WordPress platform. When he's not coding PHP, Perl, CSS, JavaScript, or whatnot, he spends time with his wife, three children, a dog, and a cat in their Atlanta area home.
This entry was posted in Development, Tech and tagged , . Bookmark the permalink.

5 Responses to Perl geekery: building hashes

  1. Dougal says:

    Now, now, let’s not turn this into a religious discussion 🙂

    However, I have been kicking around some ideas for a post about OOP zealotry…

  2. Matt M. says:

    I just thought I’d throw in a couple more ways to tackle things like this:


    {
    my ($foo,$bar,$baz) = ('foo val','bar val','baz val');

    my @keys = qw/foo bar baz/;
    my @values = ($foo,$bar,$baz);

    # You can use map like the foreach
    my %hash = map { $keys[$_] => $values[$_] } 0 .. $#keys;
    my %hash = map { $keys[$_] => shift @values } 0 .. $#keys;
    my %hash = map { $_ => shift @values } @keys;
    my %hash = map { $_ => shift @values } qw/foo bar baz/;

    # I tend to use hashrefs for passing stuff around
    my $hash = {'foo'=>$foo,'bar'=>$bar,'baz'=>$baz};
    print $hash->{'foo'};

    # Plus with hashrefs you can make them really complex and
    # still return them from a sub

    }

  3. Dougal says:

    Hey, Matt! Long time no see!

    I thought about using map, but I was mainly illustrating the evolution of the refactoring that I actually did. Plus, the map method isn’t as clear or elegant, at least to my eyes (except for maybe your third example).

    And yes, I use a lot of hashrefs for building complex data structures. Hashrefs — they’re what’s for dinner!

Leave a Reply

%d bloggers like this: