Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

jdporter (36)

Journal of jdporter (36)

Thursday June 27, 2002
11:34 PM

doodling...

[ #6033 ]

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.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.