Perl hashes and a discussion on "smart match"

Below is some code I wrote a while ago for an application that had been written in Perl.I was in a situation where I had to track dependencies on user actions. As an example:This action…Can NOT be completed before these actionsaction_4action_1, action_2, action_3action_3action_2action_2action_1But of course I wanted to give the user the power to specify any action they wanted. (including a list of actions) So, if the code was told to do ‘action_4’, it would look that action up, see that actions 1, 2, and 3 needed to be completed first, and then do them. But this would have to be recursive as you can see that ‘action_3’ depends on 2, and 2 depends on 1. If ‘action_1’ is run, we also want to look it up and remove it from this list, since it is no longer a dependency for other actions.So I created a simple hash to keep track of all the dependencies (they can sometimes change, so it was nice to have them all in one spot.) Below is the method I ended up creating to manage those dependencies. I made it generic enough to handle any hash you feed it. It also accepts an option where you can tell it how you want it to remove dependencies.# Usage: remove_hash_deps([var], [hash list], [run type])# Function: Will take the string provided in the first argument and see if it exists in the provided hash.# If it exists, it will do one of the following depending on the 3rd argument option:# this_one - Removes any hash that has the string as the key.# on_this - Removes any hash where the string appears somewhere in the value.# all - Does the first two above, but will recursively do the “on_this” removal.# by taking the keys that come from matches during the “on_this” removal.sub remove_hash_deps { my $target = shift; my $hash = shift; my $type = shift; my @removed; if ($type eq “all” || $type eq “this_one”) { if (exists $hash->{$target}) { delete($$hash{$target}); unshift @removed, $target; } } if ($type eq “all” || $type eq “on_this”) { #my @matches = grep {$$hash{$_} ~~ /($target)/} keys %$hash; my @matches; foreach my $key (keys %$hash) { my $value = $$hash{$key}; if ($value =~ /($target)/) { #does $value CONTAIN a match of the following regex unshift @matches, $key; } } foreach my $match (@matches) { delete($$hash{$match}); unshift @removed, $match; if ($type eq “all”) { my @rec_removed = remove_hash_deps($match, $hash, $type); unshift @removed, @rec_removed; } } } return @removed;}Seasoned Perl developers will notice line:23. That single line reminded me of a valuable software development lesson:“Always match your development environment to production”That line makes use of “smart match” (~~), a useful little Perl feature added in version 5.10.0 in December 2007. That one line does all of the work of the foreach loop that follows it. There was just one problem: production wasn’t using Perl 5.10.0 and wasn’t going to have it for some time. As a result, I had to throw together that foreach statement at the last minute to replace that line of code.All too often you see developers with two or three large screen monitors for developing. Often, they’ll also be given a huge development box with the latest Java, Perl, and any other tools they need or ask for. They then write their code to work in the latest version of Chrome or Firefox, and then marvel at how well everything looks at 1920x1080 resolution.Then, everyone is shocked when they find out that users in production are still using IE6 and have a screen resolution of 800x600.Some developers will take this as an important life lesson, but I also find that many will just dismiss this as “something test team should deal with” or “that’s what UAT testing is for”. Obviously no developer wants to be denied the latest and greatest development setup, but I think, if nothing else, each developer needs quick and easy access to somewhere they can view their code as if they were a production user.And if you think this doesn’t happen often, my fiancee just told me she had this same issue a couple of months ago with her development team where a Regex that worked fine in Java 7, wasn’t compatible with Java 6 and caused things to break.This question of what to support and when is a bit more complicated than it at first seems, so I’ll save more thoughts on this subject for another time.

Edward Romano Written by:

I dabble in, and occasionally obsess over, technology and problems that bug me