检查允许值是错误的。
该grep
函数接受一个代码块和一个列表。它$_
依次将变量设置为列表中的每个元素。如果块返回真值,则保留该元素。在标量上下文中,grep
不返回保留元素的列表,而是返回计数。
你的grep
块是{!$args->{$k}}
. 当$args->{$k}
为假时返回真,反之亦然。结果不依赖于$_
,因此不检查参数是否是允许值之一。
要查看给定值是否为允许值,您必须测试某种形式的等价性,例如
if (grep { $args->{$k} eq $_ } @{ $opts->{$k}{allowed} }) {
# this is executed when the arg matches an allowed value
} else {
# the arg is not allowed
}
智能匹配和列表之旅:: MoreUtils
如果您可以使用 perl > v10,则可以使用智能匹配。这会将上述条件表示为
use 5.010;
$args->{$k} ~~ $opts->{$k}{allowed}
可能的类型组合的冗长表格表明,这大致相当于grep
如果 arg 是标量(字符串/数字),并且允许的 arrayref 也仅包含普通标量。
然而,智能匹配在 v18 中被重新标记为实验性的,并且行为可能很快就会改变。
与此同时,坚持显式grep
等可能会更好。但我们可以实现两个改进:
将grep
测试所有元素,即使已经找到匹配项也是如此。这可能是低效的。核心模块中的first
函数与List::Util
具有相同的语法grep
,但在第一个元素之后停止。如果块匹配一个值,则返回该值。如果没有值匹配,则返回undef
。这使得事情变得复杂,什么时候undef
可能是一个有效的值,或者甚至允许错误的值。但在你的情况下,grep
可以替换为
use List::Util 'first';
defined first { $_ eq $args->{$k} } @{ $opts->{$k}{allowed} }
List::MoreUtils 模块具有更多功能。例如,它提供了any
对应于数学∃(存在)量词的函数:
use List::MoreUtils 'any';
any { $_ eq $args->{$k} } @{ $opts->{$k}{allowed} }
这仅返回一个布尔值。虽然它可能不如普通的grep
or有效first
,但 usingany
是非常自我记录的,并且更易于使用。
到目前为止,我一直假设我们只会对允许的值进行字符串比较。这有时会起作用,但最好指定一个显式模式。例如
croak qq(Value for "$k": "$args->{$k}" not allowed) unless
$opts->{$k}{mode} eq 'str' and any { $args->{$k} eq $_ } @{ $opts->{$k}{allowed} }
or $opts->{$k}{mode} eq 'like' and any { $args->{$k} =~ $_ } @{ $opts->{$k}{allowed} }
or $opts->{$k}{mode} eq 'num' and any { $args->{$k} == $_ } @{ $opts->{$k}{allowed} }
or $opts->{$k}{mode} eq 'smart' and any { $args->{$k} ~~ $_ } @{ $opts->{$k}{allowed} }
or $opts->{$k}{mode} eq 'code' and any { $args->{$k}->($_) } @{ $opts->{$k}{allowed} };
防止未知选项
您可能希望也可能不希望在$args
哈希中禁止未知选项。特别是如果您考虑类的可组合性,您可能希望忽略未知选项,因为超类或子类可能需要这些选项。
但是,如果您选择检查错误的选项,您可以delete
使用您已经处理过的那些元素:
my $self = {};
for my $k (keys %$opts) {
my $v = delete $args->{$k};
...; # use $v in the rest of the loop
$self->{$k} = $v;
}
croak "Unknown arguments (" . (join ", ", keys %$args) . ") are forbidden" if keys %$args;
或grep
未知参数:
my @unknown = grep { not exists $opts->{$_} } keys %$args;
croak "Unknown arguments (" . (join ", ", @unknown) . ") are forbidden" if @unknown;
for my $k (keys %$opts) {
...;
}
或者您可以遍历$args
and的组合键$opts
:
use List::Util 'uniq';
for my $k (uniq keys(%$opts), keys(%$args)) {
croak "Unknown argument $k" unless exists $opts->{$k};
...;
}
标量上下文
我假设您正确初始化$args
为哈希引用:
my $args = { file => 'file.txt', type => 'foo', head => 1 };
使用 parens 而不是 curlies 在语法上是有效的:
my $args = ( file => 'file.txt', type => 'foo', head => 1 );
但这不会产生哈希。相反,=>
and的,
行为类似于 C 中的逗号运算符:评估并丢弃左操作数。也就是说,只保留最后一个元素:
my $args = 1; # equivalent to above snippet.