I wanted to have the convenience of comma separated multivalued options while using Getopt::Long. The idiom for that is given in the pod:

my @libfiles; GetOptions ("library=s" => \@libfiles); @libfiles = split(/,/,join(',',@libfiles));
Trouble was, I had a lot of those options. Nonetheless, I just bulled ahead with:
use Getopt::Long; my (@foos, @bars, @bazen, @quuxi); GetOptions ( 'foos=s' => \@foos, 'bars=s' => \@bars, 'bazen=s' => \@bazen, 'quuxi=s' => \@quuxi ); # Obnoxious first cut podded out =begin comment @foos = split /,/, join ',', @foos; @bars = split /,/, join ',', @bars; @bazen = split /,/, join ',', @bazen; @quuxi = split /,/, join ',', @quuxi; =end comment
That's repetitive. I started looking for a less redundant way to get that done.

My first thought was of a subroutine.
=begin comment sub c_split { split /,/, join ',', @_; } @foos = c_split(@foos); @bars = c_split(@bars); @bazen = c_split(@bazen); @quuxi = c_split(@quuxi); =end comment
Still redundant, and more lines of code. There is also the addition of c_split to the namespace.

The next idea was to have c_split act on the argument, passed by reference.

=begin comment sub c_split (\@) { my $aref = shift; @$aref = split /,/, join ',', @$aref; } c_split(@foos); c_split(@bars); c_split(@bazen); c_split(@quuxi); =end comment
That eliminates the assignments, but all the other objections remain.

Next idea was to pass all the arrays by reference and act on them in place.

=begin comment sub c_split { @$_ = split /,/, join ',', @$_ for @_; } c_split(\@foos, \@bars, \@bazen, \@quuxi); =end comment
Much better, but now there is a named sub defined which is only called once. It's too specialized to be of much general use.

An anonymous sub? You bet! Here's what I ended up with:

(sub { @$_ = split /,/, join ',', @$_ for @_ }) -> (\@foos, \@bars, \@bazen, \@quuxi);
Zero redundancy and no namespace pollution.
I wonder if I went too far with that? ;-)

(Added) ++calin, I knew I was getting obsessive, but I didn't realize I had the blinders on!

After Compline,
Zaxo

Replies are listed 'Best First'.
Re: Getting Sparse - Taming Multivalued Options in Getopt::Long
by calin (Deacon) on Jun 06, 2004 at 17:38 UTC

    You don't need an anonymous sub. A foreach will do:

    @$_ = split /,/, join ',', @$_ for (\@foos, \@bars, \@bazen, \@quuxi);

Re: Getting Sparse - Taming Multivalued Options in Getopt::Long
by hossman (Prior) on Jun 06, 2004 at 23:27 UTC

    why use two passes? Give GetOptions a sub to call instead.

    use Getopt::Long; my (@foos, @bars, @bazen, @quuxi); GetOptions ( 'foos=s' => sub { shift; push @foos, split /,/, @_; }, 'bars=s' => sub { shift; push @bars, split /,/, @_; }, 'bazen=s' => sub { shift; push @bazen, split /,/, @_; }, 'quuxi=s' => sub { shift; push @quuxi, split /,/, @_; }, );

    of course, since your option names exactly match your arrays, something like this should work...

    use Getopt::Long; my (@foos, @bars, @bazen, @quuxi); sub setter { no strict refs; push @{shift}, split /,/, @_; } GetOptions ( 'foos=s' => \setter, 'bars=s' => \setter, 'bazen=s' => \setter, 'quuxi=s' => \setter, );

    but at that point you should just use...

    use Getopt::Long; my %opts; sub setter { push @{$opts{shift}}, split /,/, @_; } GetOptions ( 'foos=s' => \setter, 'bars=s' => \setter, 'bazen=s' => \setter, 'quuxi=s' => \setter, );

    (All of that is completely untested ... I'm sure I have some syntax typos).

    Update: Yeah, What Roy said....

      ...which leads to
      GetOptions ( map {"$_=s" => \setter} qw(foos bars bazen quuxi) );
      right?

      The PerlMonk tr/// Advocate
      my (@foos, @bars, @bazen, @quuxi); sub setter { no strict refs; push @{shift}, split /,/, @_; }
      What's the point of the my? If you are using symbolic references, you will work with package variables, not lexical variables (well, that's assuming you use @{+shift}, otherwise you'll be pushing on @shift).

      Abigail