doodling...

jdporter on 2002-06-28T04:34:21

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.