walkingthecow has asked for the wisdom of the Perl Monks concerning the following question:

I have a subroutine in a perl module and I would like to know best way to have default values, or values that can be passed but have no defaults. For example, sometimes you want to set BOOTPW, but not always, and there really is no default value -- either it is set, or it is not. So, instead of having a default value, how would I set it up to accept it as a parameter without a default value? Also, there is no default value for ID, but not sure how to do that either. However, for some flags there IS a default value, such as exptm which has a default value of -1, but can be changed with parameters. Anyway, here's the code that I have thus far:

sub setAgeHP { my %defaults = (qw(mintm -1 expwarn -1 lftm -1 acctexp -1 exptm -1 + llog -1 bootpw NO umaxlntr -1 id null)) my %args = (%defaults, @_); my $return; if ($args{'pwdage'} == 30) { $hpmod="/usr/lbin/modprpw -k -m "; } else { $hpmod="/usr/lbin/modprpw -m "; } print $rsh "$hpmod $args{id};echo \$?\r"; ($match_num,$error,$match,$before,$after)= $rsh->expect($TIME_OUT, '-re', '^[1-9][0-9][0-9]\r?$' ,'-re', '^[1-9][0-9]*\r?$' ,'-re', '(?s).*\r\n0\r?$'); if ($match_num == 3) { $result = "SUCCESSFUL"; } else { $result = "FAILED with exit code $match"; } return $result; }

Replies are listed 'Best First'.
Re: Default Values in Subroutines
by ig (Vicar) on Nov 04, 2008 at 04:02 UTC

    You can also use exists to distinguish whether hash entries exist. If you don't set a default and the caller does not provide a value, then the entry will not exist.

    The following code:

    #!/usr/bin/perl use strict; use warnings; my %hash = ( a => 1, b => undef); foreach my $key ('a', 'b', 'c') { print "$key " . (defined($hash{$key})?"is":"is not") . " defin +ed\n"; print "$key " . (exists($hash{$key})?"does":"does not") . " ex +ist\n"; }

    produces the following output:

    a is defined a does exist b is not defined b does exist c is not defined c does not exist
Re: Default Values in Subroutines
by ysth (Canon) on Nov 04, 2008 at 05:10 UTC
    If something doesn't have a default, just omit it from your %defaults hash. Or specify its value as undef (but not in qw(), since that would produce the string "undef"). But in the case of id, you are interpolating it in a string; what do you want there if it isn't provided? I'm guessing nothing, which you would accomplish by giving an empty string as the default (also not possible in qw()):
    my %defaults = ( ..., 'id' => '', ... );
    Your example doesn't use bootpw, so I'm not sure what you want for that.
Re: Default Values in Subroutines
by repellent (Priest) on Nov 04, 2008 at 03:20 UTC
    You can use undef as the default value. See undef and defined.
    my %defaults = ( bootpw => undef ); # is bootpw set to default? if (defined $defaults{bootpw}) { print "Yes!\n"; } else { print "No.\n"; }
Re: Default Values in Subroutines
by dHarry (Abbot) on Nov 04, 2008 at 08:17 UTC

    Get yourself a copy of Perl Best Practices and lookup the chapter on subroutines. It has many useful suggestions for creating better subs like how to handle named arguments, missing arguments and default argument values.

Re: Default Values in Subroutines
by dragonchild (Archbishop) on Nov 04, 2008 at 14:22 UTC
    Params::Validate was built to solve this problem.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

      You see, I want to rewrite my current subroutines and allow for default values, or null values. I was trying the code above, but can't figure out how to get it to work. You see, the command changes based upon input.

      Sometimes the command will be:
      /usr/bin/passwd -n 1 -w 5 -x 30

      Sometimes the command will be:
      /usr/bin/passwd -n 1 -w 5 -x 60

      and sometimes the command will be:
      /usr/bin/passwd -x -1

      I would like to set default values for n (1), w(5) and x(60), but also allow them to be null, like in the case of the last command... Just trying to figure out the best way to do this.

      Current Code:
      sub SUNPwdAging { my $loginid = shift; my $pwminlife = shift; my $pwmaxlife = shift; my $pwdwarn = shift; my $command; my $match_num; my $error; my $match; my $before; my $after; my $passwd = "/usr/bin/passwd"; my $options = "-n $pwminlife -x $pwmaxlife -w $pwdwarn"; # Define full command $command = "$passwd $options $loginid"; # Set the Aging $Login::rsh->clear_accum(); print $Login::rsh "$command\; echo \$?\r"; ($match_num,$error,$match,$before,$after)= $Login::rsh->expect($Login::TIME_OUT, '-re', '(?s).*\r\n[1-7]\r?$' ,'-re', '(?s).*\r\n0\r?$' ); if ($match_num == 1) { $result = "FAILED: ".$match.": ".$after; } elsif ($match_num == 2) { $result = "SUCCESSFUL"; } elsif (defined($error)) { $result = "FAILED: ".$error; } else { $result = "FAILED: Unknown error occurred."; } return $result; }
      Would like it to be something more like this...
      sub SunPwdAging { my %defaults = (qw(n 1 w 5 x 60)) my %args = (%defaults, @_); my $result; ....
      But then, how do I setup the command to even use what's passed to the subroutine?
        What about something like:
        use warnings; use strict; use Data::Dumper; namedMunge(p1=>'aValU', foo=>undef); sub namedMunge { my %ParamHash = @_; my %defaults = ( foo=>0, bar=>42, baz=>'pie'); ApplyDefaultParams(\%ParamHash, \%defaults); # Do Munge here! print Dumper \%ParamHash; } sub ApplyDefaultParams { my $ParamHashRef = shift; my $DefaultsHashRef = shift; foreach my $key (keys(%$DefaultsHashRef)) { if (not exists($ParamHashRef->{$key})) { $ParamHashRef->{$key} = $DefaultsHashRef->{$key}; } } return $ParamHashRef; }
        Resulting in:
        $VAR1 = { 'foo' => undef, 'baz' => 'pie', 'p1' => 'aValU', 'bar' => 42 };

        Maybe something like the following would work for you.

        I note that in your example you had $loginid as first argument but didn't use the variable anywhere.