2

I'm trying to make a function for a module accept optional arguments in the form of:

function(arg1, optional1 => opt1, optional2 => opt2, ..);

Yet I can't find anywhere that shows a good explanation how to do this. I came up with the following solution to accept email, alpha and html arguments, but it's extremely convoluted and I can't believe there isn't a shorter way to accomplish this:

sub test
{
    my ($s, @args) = @_;
    my $alpha = 1;
    my $html = 0;
    my $email = 0;
    for(my $i = 0; $i < scalar(@args); $i++)
    {
        if($args[$i] eq "alpha")
        {
            $i++;
            $alpha = $args[$i];
        }
        elsif($args[$i] eq "email")
        {
            $i++;
            $email = $args[$i];
        }
        elsif($args[$i] eq "html")
        {
            $i++;
            $html = $args[$i];
        }
    }
    return ($alpha, $email, $html);
}

my ($a, $b, $c) = test("stuff", ndhgffs => 1, email => 1, alpha => 0);

print $a . $b . $c;

EDIT:

Thanks to the answer below and comments below that, this solved it:

sub test
{
   my ($s, %opts) = @_;
   my $email = $opts{'email'} // 0;
   my $alpha = $opts{'alpha'} // 1;
   my $html = $opts{'html'} // 0;
   return ($alpha, $email, $html);
}
G. Cito
  • 6,210
  • 3
  • 29
  • 42
Dendory
  • 620
  • 2
  • 10
  • 21
  • The other way of doing it is passing a hash reference. – Sobrique Dec 16 '14 at 15:36
  • Is `alpha` boolean? If so, `$opts{'alpha'} // 1` is error prone. Consider `$opts{'no_alpha'} // 0` (or just `$opts{'no_alpha'}`). – ikegami Dec 16 '14 at 15:54
  • OT but how would one handle this with function signatures? Could post 5.20 `use experimental 'signatures'` offer a cleaner "idiom" for this sort of thing? It seems like grabbing things from `@_` is pretty neat/clean and self documenting when you have complicated function arguments ... TIMTOWTDI && ♥ perl :-) – G. Cito Dec 16 '14 at 16:05
  • In this example yes the functions would be Boolean, although I assume `//` should work with strings as well.. – Dendory Dec 16 '14 at 16:13
  • Possible duplicate of [How to pass optional parameters to a Perl function?](https://stackoverflow.com/questions/8124138/how-to-pass-optional-parameters-to-a-perl-function) – Pablo Bianchi Apr 10 '18 at 01:24

2 Answers2

4
sub function {
   my $arg1 = shift;
   my $arg2 = shift;
   my %opts = @_;
   my $optional1 = delete($opts{$optional1});
   my $optional2 = delete($opts{$optional2});
   croak("Unrecognized parameters ".join(' ', keys(%opts))) if %opts;
   ...
}

or

sub function {
   my ($arg1, $arg2, %opts) = @_;
   my $optional1 = delete($opts{$optional1});
   my $optional2 = delete($opts{$optional2});
   croak("Unrecognized parameters ".join(' ', keys(%opts))) if %opts;
   ...
}

Notes:

  • If you don't want to bother checking for unrecognized options, you don't need delete.

  • You can easily assign default values when an argument is omitted or undef using

    my $arg = delete($opts{$arg}) // 'default';   # Perl 5.10+
    
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Should one use `delete` in this case? – raina77ow Dec 16 '14 at 15:20
  • 4
    Habit. It allows `die "Unrecognized args" if %opts;`. If you don't use that check, you don't need `delete`. – ikegami Dec 16 '14 at 15:20
  • 1
    Shouldn't have edited your comment, your original one was good, I didn't know about '//'. – Dendory Dec 16 '14 at 15:24
  • @Dendory I'm pretty sure `//` is a post `5.10` thing - IOW it's a new modern perl feature from the early part of the millenium(does that make it a mature in Perl terms?). If you maintain business critical applications running on an untouchable `5.8.*` installation it's normal you would not know about it ... in fact you maybe should try to forget ;-) – G. Cito Dec 16 '14 at 15:45
  • Actually there might have been patches for 5.8.* or `//` could have been back ported. – G. Cito Dec 16 '14 at 15:46
  • 1
    @G. Cito, Someone still using 5.8 won't be patching it to add `//`. (A patch actually does exist.) – ikegami Dec 16 '14 at 15:56
0

If the subroutine arguments are copied from the values of @_ and assigned to variables for the function "in order of appearance", then your function could be written to either have defaults you could set with the defined OR operator // or, if those values are truly optional and not always needed, then you could assign a placeholder value (e.g. not_set) that you then remove/delete after the function reads in its arguments from @_ with something like:

for (keys %opthash){ if ($opthash{$_} =~ /not_set/)  {delete $opthash{$_} }}

I'm not sure this is the most efficient approach if this function is one that is used a lot.

G. Cito
  • 6,210
  • 3
  • 29
  • 42