子程序中的默认参数值

14

我不知道如何为子程序设置默认参数。这是我的考虑:

sub hello {
  print @_ || "Hello world";
}

如果你只需要一个参数,那么这样做很好。如果你需要为多个参数设置默认值呢?

我原本打算这样做:

sub hello {
  my $say = $_[0] || "Hello";
  my $to  = $_[1] || "World!";
  print "$say $to";
}

但那是很多工作... 必须有更简单的方法;可能是最佳实践吗?

9个回答

15

我使用命名参数来完成它,像这样:

sub hello {
    my (%arg) = (
        'foo' => 'default_foo',
        'bar' => 'default_bar',
        @_
    );

}

我相信Params::Validate支持默认值,但那比我想要承担的麻烦更多。


13

通常我会做类似于以下的操作:

sub hello {
    my ($say,$to) = @_;
    $say ||= "Hello";
    $to ||= "World!";
    print "$say $to\n";
}

请注意,从 Perl 5.10 开始,您可以使用 "//=" 运算符来测试变量是否已定义,而不仅仅是非零。(想象一下调用 hello("0","friend"),使用上述内容将产生 "Hello friend",这可能不是您想要的结果。使用 //= 运算符将产生 "0 friend")。


4
@Davidmoreen:测试数组的定义是否有用。 - ysth
3
@Davidmoreen,正确的说法是print $_[0] // "Hello world";。正如ysth所说,你不能在数组上使用defined(这就是//的含义),因为在标量上下文中,一个数组返回其长度,它总是被定义的。 - cjm

7
请查看Method::Signatures。它使用Devel::Declare提供了一些额外的(必要的!)语法糖,关键字为methodfunc

下面是使用新的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/


5
如果您查看《Perl最佳实践》文档中的“默认参数值”部分,作者Damian Conway提出了一些重要观点,例如:
- 尽早解析任何默认参数值,即在@_展开时。 - 建议将多个默认值因子分解为表格(即哈希表),然后使用该表格预初始化参数哈希表,这是最清晰的方法。
例如:
#!/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

如需更多详情和完整示例,请参考Perl最佳实践


4

我最喜欢的方法是:自Perl 5.10版本以来,您可以使用//检查变量是否已定义,并在未定义时提供替代值。因此,一个简单的示例是:

my $DEFAULT_VALUE = 42;

sub f {
    my ($p1, $p2) = @_;
    $p1 //= 'DEFAULT';
    $p2 // = $DEFAULT_VALUE;
}

另一个选择是使用shift指令从@_中获取参数:
sub f {
    my $p1 = shift // 'DEFAULT';
}

来源:https://perlmaven.com/how-to-set-default-values-in-perl

在Perl中,我们可以使用很多种方式来设置默认值。下面是一些常用的方法:

  • 使用“||”操作符:如果变量没有被赋值,则返回该操作符右侧的默认值。
  • 使用三元运算符:如果变量没有被赋值,则返回问号之前的值,否则返回冒号之后的值。
  • 使用“//”操作符:如果变量没有被定义或者被定义但是为假值,则返回该操作符右侧的默认值。

这些方法都非常简单易懂,同时也能够有效地帮助我们在编写代码时避免出现未定义变量的错误。


4
因为Perl传递子程序的参数机制是单个列表,所以参数是按位置排列的。这使得提供默认值变得很困难。一些内置函数(例如substr)通过根据它们被使用的可能性对参数排序来处理此问题 - 不太频繁使用的参数出现在末尾并具有有用的默认值。
更简洁的方法是使用命名参数。Perl不直接支持命名参数,但可以使用哈希表模拟:
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!

注意:定义或操作符//是在Perl v5.10中添加的。它比使用逻辑或(||)更健壮,因为它不会默认为逻辑上为假的值''0

2
在CPAN上有一个名为Attribute::Default的模块。这可能比此方法更加简洁,并避免了一些复杂性(例如,如果您想将false传递给子例程怎么办?)。
我还看到过人们使用my $var = exists @_[0] ? shift : "Default_Value";,但Perl的文档指出在数组上调用exists已被弃用,所以我不推荐使用它。 Attribute::Default文档页面的一部分:
  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');

1

在其他答案中已经讨论了解决您问题的最佳方法。
但有一件事情引起了我的注意,那就是您说:

sub hello {
   print @_ || "Hello world";
}

And that works fine for if all you needed was one argument.

你实际上尝试过那段代码吗?它将打印参数的数量,或者在没有提供参数时,打印“Hello World”!
原因是 || 运算符具有优先级,并强制左侧处于标量上下文中,从而将 @_ 缩减为您提供的参数数量,而不是参数本身!
请查看 perlop 以获取有关 Perl 中运算符的更多信息。

希望对你有所帮助,
保罗


0

如需更多信息,请参见Method::Signatures

func add($this = 23, $that = 42) {
    return $this + $that;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接