Perl支持函数调用中的命名参数吗?

Perl支持函数调用中的命名参数吗?,perl,named-parameters,Perl,Named Parameters,根据我对支持该功能的语言的经验,使用命名参数而不是位置参数调用函数的程序更易于阅读和维护 我认为Perl有这个特性,但它不适合我 这是我使用的软件包的一个怪癖,还是我做错了 设置函数调用 我的第一个Perl项目是使用HTML::TableExtract包从HTML标记中提取表数据并将其显示为文本 以下代码设置解析器: use strict; use warnings; use HTML::TableExtract; my $markup = <<MARKUP; <table&

根据我对支持该功能的语言的经验,使用命名参数而不是位置参数调用函数的程序更易于阅读和维护

我认为Perl有这个特性,但它不适合我

这是我使用的软件包的一个怪癖,还是我做错了

设置函数调用 我的第一个Perl项目是使用HTML::TableExtract包从HTML标记中提取表数据并将其显示为文本

以下代码设置解析器:

use strict;
use warnings;
use HTML::TableExtract;

my $markup = <<MARKUP;
<table>
  <tr> <th>a</th> <th>b</th> <th>c</th> </tr>
  <tr> <td>1</td> <td>2</td> <td>3</td> </tr>
  <tr> <td>4</td> <td>5</td> <td>6</td> </tr>
</table>
MARKUP

my $parser = HTML::TableExtract->new() ;

$parser->parse($markup) ;
列之间用下划线分隔,而不是默认的冒号:

TABLE(0, 0):
a_b_c
1_2_3
4_5_6
在Perl.com之后,我尝试传递一个包含参数名和值的哈希,以澄清参数的含义:

$parser->tables_dump({show_content => 1, col_sep => '_'}) ;
Perl不理解这一点。它忽略
col_sep
的值,并以默认值输出:

TABLE(0, 0):
a:b:c
1:2:3
4:5:6
如果不尝试更改分隔符,则得到相同的输出:

$parser->tables_dump({show_content => 1}) ;
即使我指定了无意义的参数名称,也会得到相同的输出:

$parser->tables_dump({tweedledum => 1, tweedledee => '_'}) ;

我可以使用命名参数样式调用此函数,还是只满足于位置式?

Perl没有内置的命名参数支持。如果要使用它们,必须专门编写函数以接受该样式的参数。因此,您必须满足于位置参数(或编写包装函数(可能在子类中))。

Perl本机不支持命名参数,但是可以设计函数来接受命名参数(作为哈希或hashref)。这取决于函数的作者如何实现它。您需要提供函数期望的参数,否则会得到意外的结果。

这并不太复杂。可以像散列或散列引用一样传递键值对,并在子例程中将参数加载到散列或散列引用中

# called like $parser->tables_dump({show_content => 1, col_sep => '_'}) ;
sub TheParser::tables_dump {
    my ($self, $args) = @_;
    if ($args->{show_content} == 1) {
        print join $args->{col_sep}, $self->the_column_data();
        ...
    }
}
通过另一行,您可以将已知的命名参数加载到适当命名的变量中:

    my ($self, $args) = @_;
    my ($show_content, $col_sep) = @$args{qw(show_content col_sep)};
    if ($show_content == 1) {
       ...

下面是我编写的一个简单程序,它可以使用一种命名参数。它允许默认值

#!/usr/bin/perl
use strict;
use warnings;
use 5.014;
use POSIX qw/ strftime /;

# Script to get prior Monday (if today is Mon, then Mon a week ago).

for my $day (qw/ Sun Mon Tue Wed Thu Fri Sat /) {
    say "Last $day: ", last_monday( day => $day );  
}

sub last_monday {
    my %arg =  ( time => [localtime],
                 day  => 'mon',
                 span => 1,
                 @_
               );
    my $dow; # day of week

    if    ('sunday'    =~ /$arg{day}/i) { $dow = 0}
    elsif ('monday'    =~ /$arg{day}/i) { $dow = 1}
    elsif ('tuesday'   =~ /$arg{day}/i) { $dow = 2}
    elsif ('wednesday' =~ /$arg{day}/i) { $dow = 3}
    elsif ('thursday'  =~ /$arg{day}/i) { $dow = 4}
    elsif ('friday'    =~ /$arg{day}/i) { $dow = 5}
    elsif ('saturday'  =~ /$arg{day}/i) { $dow = 6}
    else {
        warn "$arg{day} is not a valid day of week. $!";
        return;
    }

    my ($wday, @dmy) = @{ $arg{time} }[6, 3..5];

    # (will work across month, year boundries)
    $dmy[0] -= ($wday - $dow) % 7 || ($arg{span} ? 7 : 0); # $dmy[0] == mday
    return strftime "%Y%m%d", 0,0,0,@dmy;
}
在Perl.com的Advance Subroutines文章之后,我尝试传递一个 包含参数名称和值的哈希,以澄清 参数:

$parser->tables_dump({show_content => 1, col_sep => '_'}) ;
这篇文章介绍了一种编写子例程的方法,使它们能够接受命名参数的hashref。如果您正在调用一个未编写为接受的sub,那么它将不知道如何正确处理它

Perl不理解这一点。它忽略col_sep和 具有默认值的输出:

TABLE(0, 0):
a:b:c
1:2:3
4:5:6
不要过于迂腐,但Perl很好地理解这一点。但是,
tables\u dump
仅用于接受标量参数列表。当您这样调用它时,它将接收单个标量参数。此参数恰好是对散列的引用,但
tables\u dump
不知道或不关心这一点,因此它使用该引用作为
$show\u content
的值。这可能相当于将
1
传递给
show\u content
,因为
1
和任何可能的引用在布尔上下文中都将计算为“true”,我假设
$show\u content
仅用作布尔值


由于没有第二个参数,因此不会将任何内容分配给
$col_sep
,因此,正如您所观察到的,它使用默认分隔符。

在第6章中对传入Perl的命名参数(即使使用默认值)进行了很好的解释。 这种样式非常重要,在对象构造函数中广泛使用。这就是为什么在他们的OO Perl书中对此进行了解释

我会引用其中两个例子:

# This is how you call a subroutine using named argument passing
interests(name => "Paul", language => "Perl", favourite_show => "Buffy");

# This is how you define the subroutine to accept named arguments
sub interests {
   my (%args) = @_;

   # This is how you capture named arguments and define
   # defaults for the ones missing from a particular call.
   my $name           = $args{name}           || "Bob the Builder";
   my $language       = $args{language}       || "none that we know";
   my $favourite_show = $args{favourite_show} || "the ABC News";

   print "${name}’s primary language is $language. " .
   "$name spends their free time watching $favourite_show\n";
}
另一个给出定义默认值(在散列中)的不同方法的示例如下:


离开标题的问题,您可以执行以下操作:

use strict;
use Carp::Assert;

sub func {
   my (%hash) = @_;
   assert($hash{'baz'} == 1);
   assert($hash{'mu'} == 2);
}

func('baz' => 1, 'mu' => 2);

对不起,我不明白。您是否正在演示如何使用接受命名参数的方法包装模块方法?否,这是如何编写需要命名参数的新方法。对不起,如果这不是你想要的。我不是模块的维护者。我很乐意接受它的局限性。在Perl之前我有很多东西要学习,我甚至会考虑为别人的代码提交补丁。尽管如此,谢谢你的帮助!您的代码片段给了我一些学习指导。感谢您解释观察到的行为。你是对的,但是Perl的理解不是我的意思!说到底,是我不懂Perl。你能在回答中多用些英语吗?我对Perl的理解还不够透彻,根本看不出这是如何回答我的问题的。@isme为这个糟糕的答案感到抱歉isme。我没有仔细阅读你的问题。如果我可以删除我的帖子,我会的。我不是在寻找一种方法来创建一个接受命名参数的模块;我认为HTML::TableExtract模块已经接受了命名参数。这个问题基于一个错误的前提。然而,当我开始编写自己的Perl模块时,面向对象的Perl文档将非常有用。谢谢分享。
use strict;
use Carp::Assert;

sub func {
   my (%hash) = @_;
   assert($hash{'baz'} == 1);
   assert($hash{'mu'} == 2);
}

func('baz' => 1, 'mu' => 2);