Well, I finally decided that it has been waay too long since I put a module on CPAN, so went about making one that I've been thinking about for a while.
Basically, sometimes you want to do something with one of several arrays (or hashes) based on some discriminant. For example, you have have a list of filenames, and you want to stick all the file names in one array and directory names in another. Ordinarily you might do this:
for ( @names ) {
if ( -d $_ ) {
push @dirs, $_;
} else {
push @files, $_;
}
}
Or perhaps:
@a = ( \@files, \@dirs );
for ( @names ) {
push @{ $a[ -d $_ ] }, $_;
}
If you think you're clever, you might do this:
for ( @names ) {
push @{ -d $_ ? \@dirs : \@files }, $_
}
But what I want is to do this:
for ( @names ) {
push @x, $_
}
and have @x be magically aliased to the correct array.
So I wrote a module which does this. It uses tie, of course.
tie my @x, 'Tie::Multiplex',
sub { -d $_ },
[ \@files, \@dirs ];
Now the above form will Just Work.
The first argument after the the tie class name is a sub-ref which is called by the object to get an index value. The next argument is an array(ref) of array-refs. The result of calling the sub is used to select one of the arrays in that anonymous array. In the above example, -d returns either true or false, which get converted into numeric (0 or 1) for indexing into the array.
You could have more than two, and you could have a hash of arrays instead of an array of arrays:
tie my @x, 'Tie::Multiplex', \$proto,
{
http => \@http,
ftp => \@ftp,
mailto => \@mailto,
};
for ( @URLs ) {
$proto = extract_proto $_;
push @x, $_;
}
There, I didn't use a sub, I just supplied a ref to the variable to be queried directly.
Also you can multiplex hashes instead of arrays:
tie my %x, 'Tie::Multiplex',
sub { is_word_valid $_ },
[ \%invalid_words, \%valid_words ];
for ( @words ) {
$x{$_}++;
}
That way, %valid_words ends up with keys which are all the valid words.
What happens if your selector sub returns an index which is out of range?
my @arrays = ( \@present, \@absent );
tie my @x, 'Tie::Multiplex',
sub { system("grep 'foo' $_")>>8 },
\@arrays;
# ordinarily, grep just returns 0 if found,
# 1 if not found.
for ( @files ) {
push @x, $_;
}
# but if the file does not exist, grep returns 2, and we don't have that many arrays to push onto.
By default, the Tie::Multiplex object will autovivify the new member. (The reason for the @arrays variable above is so that you could actually go get the newly-made sub-arrays if you're interested.)
But if you want to be strict, you ask Tie::Multiplex to barf on out-of-bounds conditions, by passing a true value as a third arg:
tie @x, 'Tie::Multiplex', \$i, \@arrays, 'BARF';
I think it's purty sweet, but I am skeptical that anyone would want to use it.
But then, maybe I'm just not visionary enough to foresee the ends to which someone might be inspired to use such a module.