根据我对支持该功能的语言的体验,使用命名参数而不是位置参数调用函数的程序更易于阅读和维护。
我认为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) ;
documentation表示我可以使用tables_dump
方法将输出转储到命令提示符,并使用参数$show_content
和$col_sep
来控制输出格式:
tables_report([$show_content, $col_sep])
返回一个字符串,汇总提取的表格及其深度和计数。可选择使用$ show_content标志,该标志将转储每个表的提取内容以及由$ col_sep分隔的列。默认$ col_sep是':'。
tables_dump([$show_content, $col_sep])
与tables_report()相同,除了将信息转储到STDOUT。
如果我按文档顺序传递位置参数,我会得到我期望的输出:
$parser->tables_dump(1, '_') ;
列由下划线而不是默认冒号分隔:
TABLE(0, 0):
a_b_c
1_2_3
4_5_6
在Perl.com的Advance Subroutines article之后,我尝试传递包含参数名称和值的哈希,以阐明参数的含义:
$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 => '_'}) ;
我可以使用命名参数样式调用此函数吗,还是应该选择位置?
答案 0 :(得分:11)
Perl本身不支持命名参数,但是可以设计函数来接受命名参数(作为散列或hashref)。这取决于它们如何实现它的函数的作者。你需要提供函数所期望的参数,否则你会得到意想不到的结果。
答案 1 :(得分:6)
在Object Oriented Perl第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";
}
另一个给出不同方法来定义默认值(在哈希中)的例子是:
my %defaults = ( pager => "/usr/bin/less", editor => "/usr/bin/vim" );
sub set_editing_tools {
my (%args) = @_;
# Here we join our arguments with our defaults. Since when
# building a hash it’s only the last occurrence of a key that
# matters, our arguments will override our defaults.
%args = (%defaults, %args);
# print out the pager:
print "The new text pager is: $args{pager}\n";
# print out the editor:
print "The new text editor is: $args{editor}\n";
}
答案 2 :(得分:4)
Perl没有对命名参数的内置支持。如果要使用它们,则必须专门编写该函数以接受该样式的参数。因此,您必须解决位置参数(或编写包装函数(可能在子类中))。
答案 3 :(得分:4)
按照Perl.com的高级子程序文章,我试图传递一个 hash包含参数名称和值以阐明其含义 参数:
该文章介绍了一种编写子程序的方法,使它们接受命名参数的hashref。如果您正在调用一个未被接受的子记录,那么它就不会知道如何正确处理它。
$parser->tables_dump({show_content => 1, col_sep => '_'}) ;
Perl并不了解这一点。它忽略了col_sep和的值 输出的默认值为:
不要过于迂腐,但Perl明白这很好。但是,tables_dump
仅用于接受标量参数列表。当你这样调用它时,它会收到一个标量参数。此参数恰好是对哈希的引用,但tables_dump
并不知道或关心哈希,因此它使用引用作为$show_content
的值。这可能相当于为1
传递show_content
,因为1
和任何可能的引用都将评估为&#34; true&#34;在布尔上下文中,我假设$show_content
只被用作布尔值。
由于没有第二个参数,因此没有任何内容被分配给$col_sep
,因此它会使用您观察到的默认分隔符。
答案 4 :(得分:3)
这不是太复杂。您可以像散列或散列引用一样传递键值对,并在子例程中将参数加载到散列或散列引用中。
# 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) {
...
答案 5 :(得分:2)
只关注标题问题,您可以执行以下操作:
use strict;
use Carp::Assert;
sub func {
my (%hash) = @_;
assert($hash{'baz'} == 1);
assert($hash{'mu'} == 2);
}
func('baz' => 1, 'mu' => 2);
答案 6 :(得分:-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;
}