I don't know how to set default arguments for subroutines. Here is what I considered:
sub hello {
print @_ || "Hello world";
}
That works fine for if all you needed was one argument. How would you set default values for multiple arguments?
I was going to do this:
sub hello {
my $say = $_[0] || "Hello";
my $to = $_[1] || "World!";
print "$say $to";
}
But that's a lot of work... There must be an easier way; possibly a best practice开发者_如何学运维?
I do it with named arguments like so:
sub hello {
my (%arg) = (
'foo' => 'default_foo',
'bar' => 'default_bar',
@_
);
}
I believe Params::Validate supports default values, but that's more trouble than I like to take.
I usually do something like:
sub hello {
my ($say,$to) = @_;
$say ||= "Hello";
$to ||= "World!";
print "$say $to\n";
}
Note that starting from perl 5.10, you can use the "//=
" operator to test if the variable is defined, and not just non-zero. (Imagine the call hello("0","friend")
, which using the above would yield "Hello friend"
, which might not be what you wanted. Using the //=
operator it would yield "0 friend"
).
Also have a look at Method::Signatures
. This uses Devel::Declare
to provide some extra (needed!) sugar with the keywords method
and func
.
Below is your example using the new func
:
use Method::Signatures;
func hello ($say='Hello', $to='World!') {
say "$say $to";
}
hello( 'Hello', 'you!' ); # => "Hello you!"
hello( 'Yo' ); # => "Yo World!"
hello(); # => "Hello World!"
/I3az/
If you see the documentation of Perl Best Practices: Default argument Values by Damian Conway then you will find some important points like:
- Resolve any default argument values as soon as @_ is unpacked.
- It suggest that if you have many default values to set up then the cleanest way would be factoring out the defaults into tables ie., a hash and then preinitializing the argument hash with that table.
Example:
#!/usr/bin/perl
use strict;
use warning;
my %myhash = (say => "Hello", to => "Stack Overflow");
sub hello {
my ($say, $to) = @_;
$say = $say ? $say : $myhash{say};
$to = $to ? $to : $myhash{to};
print "$say $to\n";
}
hello('Perl'); # output :Perl Stack Overflow
hello('','SO'); # output :Hello SO
hello('Perl','SO'); # output :Perl SO
hello(); # output :Hello Stack Overflow
For more detail and complete example refer Perl Best Practices.
Because Perl's mechanism for passing arguments to subroutines is a single list, arguments are positional. This makes it hard to provide default values. Some built-ins (e.g. substr
) handle this by ordering arguments according to how likely they are to be used -- less frequently used arguments appear at the end and have useful defaults.
A cleaner way to do this is by using named arguments. Perl doesn't support named arguments per se, but you can emulate them with hashes:
use 5.010; # for //
sub hello {
my %arg = @_;
my $say = delete $arg{say} // 'Hello';
my $to = delete $arg{to} // 'World!';
print "$say $to\n";
}
hello(say => 'Hi', to => 'everyone'); # Hi everyone
hello(say => 'Hi'); # Hi world!
hello(to => 'neighbor Bob'); # Hello neighbor Bob
hello(); # Hello world!
Note: The defined-or operator //
was added in Perl v5.10. It's more robust than using a logical or (||
) as it won't default on the logically false values ''
and 0
.
I like this way the most: Since Perl 5.10 you can use //
to check if a variable is defined or not and provide an alternative value in case it is not.
So, an easy example is:
my $DEFAULT_VALUE = 42;
sub f {
my ($p1, $p2) = @_;
$p1 //= 'DEFAULT';
$p2 // = $DEFAULT_VALUE;
}
Another option is using the shift
instruction to get the params from @_
:
sub f {
my $p1 = shift // 'DEFAULT';
}
Source: https://perlmaven.com/how-to-set-default-values-in-perl
There's the Attribute::Default module on CPAN. Probably cleaner than this, and avoids a couple of complexities (such as, what if you want to pass false
to your subroutine?).
I've also seen people use my $var = exists @_[0] ? shift : "Default_Value";
, but Perl's documentation notes that calling exists
on arrays is deprecated, so I wouldn't really recommend it.
Snippet of Attribute::Default
from the doc page:
sub vitals : Default({age => 14, sex => 'male'}) {
my %vitals = @_;
print "I'm $vitals{'sex'}, $vitals{'age'} years old, and am from $vitals{'location'}\n";
}
# Prints "I'm male, 14 years old, and am from Schenectady"
vitals(location => 'Schenectady');
The best way to address your problem have been discussed in the other answers.
One thing that strikes me though is that you state that:
sub hello { print @_ || "Hello world"; }
And that works fine for if all you needed was one argument.
Have you actually tried that code? It will print the number of arguments or, when none provided, Hello World
!
The reason for this is that the ||-operator takes precedence and forces the left-hand side in scalar context, thus reducing @_
to the number of arguments you provide, NOT the arguments itself!
have a look at perlop for more information on operators in Perl.
HTH,
Paul
For more sugar, see also Method::Signatures:
func add($this = 23, $that = 42) {
return $this + $that;
}
精彩评论