http://www.perlmonks.org?node_id=161091

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

This question has been on my mind quite a bit, but I haven't found a good answer anywhere online or in the O'Reilly books:

What is the preferred style for assigning default values to subroutine parameters? I have been using the following in some cases:

sub mysub { my $foo = shift || 'default'; # do stuff }

But this breaks down when '0' (or some other "false" value) is a valid parameter value. I'd like to test whether the parameter is defined, and only assign a default if it is undefined. The best I have come up with is:

sub mysub { my $foo = shift; $foo = 'default' unless defined $foo; # do stuff }
But what a pain to type. Is there a shorter/better way?

Replies are listed 'Best First'.
Re: Default subroutine parameters
by perlplexer (Hermit) on Apr 22, 2002 at 16:41 UTC
    Perhaps this may be of some use
    sub foo{ my $bar = defined $_[0] ? $_[0] : 'default'; # ... }
    --perlplexer
      Or, keeping in the spirit of this lightweight approach, i.e. not bringing a hash to bear on the problem, if you want to munch @_ with shift you will have to do something like:
      #! /usr/bin/perl -w use strict; sub x { # my $one = do { @_ ? shift : 'default' }; # my $two = do { @_ ? shift : 43 }; my $one = do { my $arg = shift; defined($arg) ? $arg : 'default' } +; my $two = do { my $arg = shift; defined($arg) ? $arg : 43 }; print "x($one, $two)\n"; } x(); x( 'this' ); x( 1, 2 );

      update: sheesh, misread a requirements document and get downvoted into oblivion. I corrected the code; you can stop now. The principal idea I wanted to show was that a do block is a pretty nice way of doing this, because the default value appears at the end of the code, making it easy to spot, thus letting you gloss over the mechanics.


      print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
        Your method doesn't take into account the fact that @_ may contain one or more undef elements.
      sub foo { my ($bar)=@_;
      $bar ||= 'default';
      }
Re: Default subroutine parameters (boo)
by boo_radley (Parson) on Apr 22, 2002 at 16:56 UTC
    or with hashes.
    &foo (bar=>"1"); sub foo { %args=@_; %defaults=(foo=>9, bar=>8, baz=>7); foreach ("foo", "bar", "baz") { defined ($args{$_}) || {$args{$_}= $defaults{$_}} ; print $args {$_} } }


    update : or better yet, with the keys that you expect the sub to know :
    &foo (bar=>"1"); sub foo { %args=@_; %defaults=(foo=>9, bar=>8, baz=>7); foreach (keys %defaults) { defined ($args{$_}) || {$args{$_}= $defaults{$_}} ; print $_ ," - ",$args {$_},"\n"; } }
    update : or better yet, just like Fletch sez. Nice stuff.
      sub foo { my %defaults = ( qw( foo 9 bar 8 baz 7 ) ); my %args = ( %defaults, @_ ); ... }
        I really like this solution because it's syntactically simple. It works when an argument is omitted entirely, but unfortunately it doesn't supply the default value when the value 'undef' is passed as an argument, like this:
        mysub(foo => 0, bar => 1, baz => undef); sub mysub { my %defaults = qw(foo 9 bar 8 baz 7); my %args = (%defaults, @_); print "Foo: $args{foo}, Bar: $args{bar}, Baz: $args{baz}\n"; }

        But it's inspired me to see if I can modify it to suit my needs. Thank you!

Re: Default subroutine parameters
by particle (Vicar) on Apr 22, 2002 at 17:08 UTC
    my preferred method is my $foo = shift // 'default';

    alas, this method won't be available until perl 6 :(

    see Exegesis 3 for more details (there's a summary on page 7.)

    ~Particle ;Þ

      I submitted a patch last night to p5p that implements //, but it's too late to make it into 5.8. However, everyone thinks it's really cool, so there's a very good chance it'll be in 5.10. (One person suggested that we fork off 5.9 right now so the patch would apply, but everyone else (including me) thinks that it'd be more trouble than it's worth to keep it in sync with the 5.8 release candidates, which should start coming out Real Soon Now.)

      Needless to say, I'm happy--it's the first time I've ever patched the core and they're already excited. :^) I thought I'd get a few "oh, that's cool"s, some style critiques, and enough experience to confidently patch the core--not the first feature for the next version of the language.

      =cut
      --Brent Dax
      There is no sig.

        congrats, BrentDax. that is *very* cool.

        ~Particle ;Þ

Explicitly check for argument presence...
by RMGir (Prior) on Apr 22, 2002 at 16:55 UTC
    sub mysub { my $foo='default'; $foo = shift if @_; }

    --
    Mike

    Delayed post: I keep hitting Newest Nodes from the preview page, then wondering why my post doesn't show up. D'oh! :)

      That's a clever way of doing it. Of course its only useful if the subroutine you are writing is expecting exactly 0 or 1 argument.

      If the subroutine allows multiple arguments with only some being optional, the above approach will not work.

        Well, it works if the optional arguments are all trailing.
        sub lotsaArgs { my $arg1 = 'default'; $arg1 = shift if @_; my $arg2 = 'default'; $arg2 = shift if @_; my $arg3 = 'default'; $arg3 = shift if @_; my $arg4 = 'default'; $arg4 = shift if @_; ... }
        This would be analogous to default arguments in C++, for instance.

        If you want to have named arguments, and only pass some of them, then the hash of default values and

        my %args=(%defaults,@_);
        as suggested below is obviously better.

        But as asked, he seemed to have only one arg to default.
        --
        Mike

Re: Default subroutine parameters
by Super Monkey (Beadle) on Apr 22, 2002 at 16:53 UTC
Re: Default subroutine parameters
by stephen (Priest) on Apr 22, 2002 at 18:52 UTC

    The "defined" method is the best way, but there's a way to have your cake and eat it too, for a price... just define a sub that processes your arguments, filling in as necessary. Like so:

    ## ## _fd() ## ## Arguments: ## ARGUMENTS: arrayref -- The arguments to a subroutine ## DEFAULTS: arrayref -- The defaults for those arguments ## ## Returns: ## list -- The elements of ARGUMENTS. Any elements in ARGUMENTS ar +e replaced ## by the corresponding element in DEFAULTS. ## sub _fd { my ($args, $defaults) = @_; my @filled = (); foreach (0 .. max($#$args, $#$defaults)) { push @filled, ( defined( $args->[$_] ) ? $args->[$_] : $defaults-> +[$_] ); } return @filled; } sub max { ($_[0] > $_[1]) ? $_[0] : $_[1]; }
    Then, in the rest of your code, you can just run your subroutines through _fd(), like so:
    ## ## test_default() ## ## Arguments: ## $name: string -- Somebody's name. (Optional) ## ## Prints "Hello my name is $name". Name defaults to "ben". ## sub test_default { my ($name) = _fd(\@_, ["ben"]); print "Hello my name is $name\n"; }

    Then, test_default('luke') prints "Hello my name is luke", while test_default() prints "Hello my name is ben". There's some overhead to calling subroutines and doing list processing all the time, but you can use it most of the time.

    Update: After thinking about this for a while, I figured out a way to use theDamian's Attribute::Handlers and a small amount of symbol-table work to give Perl a default attribute: Attribute::Default.

    use base 'Attribute::Default'; sub test_default : default('ben') { my ($name) = @_; print "Hello my name is $name\n"; }

    stephen

Re: Default subroutine parameters
by trs80 (Priest) on Apr 22, 2002 at 22:47 UTC
    Here is the manner in which I would approach this based on the (what appears to be) non OO Perl you posted. The assignment of the values could be shorten based on some of the other posts. In reviewing the other posts it dawned on me that adding anonymous hash refs might make this more confusing so apology in advance if it does.
    use Data::Dumper; use strict; # here is our default values for various # parameters. We set this up so we have # a central spot to set defaults, or # you could say a poor mans conf routine. our $defaults = { call_it => { incoming => 'text', argument => 'value', parameter => 'hello' }, call_this => { incoming => 'text', argument => 'value', parameter => 'hello' }, }; # call our sub passing an anonymous hashref call_it( { incoming => 'html' } ); sub call_it { # pull off our first value which is our hashref my $args = shift; # loop through what should be our defaults # based on our defaults hashref above foreach (keys %{$defaults->{call_it}}) { $args->{$_} ||= $defaults->{call_it}{$_} } # print out our new args # complete with our defaults and our # passed parameter preserved. print Dumper($args); } # below we move the assignment into another sub # this helps if we are going to be doing this often # another reason to do this is so that if you # make changes to how you merge the parameters # with the defaults you only have to edit this # one sub vs. replacing every copy and paste # of it. # here is our assignment function sub assign_defaults { my ($hashref,$sub) = @_; foreach (keys %{$defaults->{$sub}}) { $hashref->{$_} ||= $defaults->{$sub}{$_} } return $hashref; } # call our sub call_this( { parameter => 'good bye' } ); # this sub uses the new assign_defaults # as would any other sub that needs it sub call_this { my $args = shift; $args = assign_defaults($args,'call_this'); print Dumper($args); }
Re: Default subroutine parameters
by jeffenstein (Hermit) on Apr 23, 2002 at 09:55 UTC

    For subroutines with more than one argument:

    sub mysub { return unless( (@_ % 2) == 0); my %args = ( host => 'localhost', port => 25, @_); # print "Args: host = $args{'host'}, port = $args{'port'}\n"; 1; } mysub( host => 'gatekeeper.dec.com', port => 80) or die "Mysub failed";
Re: Default subroutine parameters
by samgold (Scribe) on Apr 23, 2002 at 03:47 UTC
    Could you check the number of arguments using $#argv and use an if statement to define it or not.
    if ($#argv == 0){ $foo = 'default'; }else { $foo = shift; }
    or something like that. I hope that helps.

    Sam
Re: Default subroutine parameters
by thelenm (Vicar) on Apr 23, 2002 at 16:15 UTC
    Thank you for all the great replies! I was hoping that maybe there was a nice syntactic shortcut I hadn't thought of, something like // in Perl6, which Particle pointed out. I also did have in mind passing multiple parameters, although my post didn't reflect that. I really appreciate everyone taking the time to post helpful answers! Thanks!
Re: Default subroutine parameters
by Popcorn Dave (Abbot) on Apr 23, 2002 at 17:29 UTC
    Wouldn't it be a better idea to set all your default parametersin your subroutine and let the values be overwritten by the information you pass in? That way you always have your defaults.