Perl 检查数组散列的散列时的混淆
我试图将我的散列输入与数据结构中允许的有效选项进行比较,如果它不是选项之一,那么我将为键设置默认值。不过,我似乎遗漏了一些东西 当前数据结构的示例Perl 检查数组散列的散列时的混淆,perl,data-structures,hash,hash-of-hashes,Perl,Data Structures,Hash,Hash Of Hashes,我试图将我的散列输入与数据结构中允许的有效选项进行比较,如果它不是选项之一,那么我将为键设置默认值。不过,我似乎遗漏了一些东西 当前数据结构的示例 my $opts = { file => { require => 1 }, head => { default => 1, allowed => [0,1], }, type => {
my $opts = {
file => { require => 1 },
head => {
default => 1,
allowed => [0,1],
},
type => {
default => 'foo',
allowed => [qw(foo bar baz)]
},
};
$args
是我的hash ref(file=>'file.txt',type=>'foo',head=>1)
我试过的一小段
for my $k ( keys %$opts ) {
croak("Argument '$k' is required in constructor call!")
if $opts->{$k}->{require} and !exists $args->{$k};
if (exists $args->{$k}) {
if (grep {!$args->{$k}} @{$opts->{$k}->{allowed}} ) {
$args->{$k} = $opts->{$k}->{default};
}
...
} else {
..set our defaults
$args->{$k} = $opts->{$k}->{default};
}
}
检查允许的值时出错
grep
函数接受一个代码块和一个列表。它会依次为列表中的每个元素设置$\uu
变量。如果块返回真值,则保留该元素。在标量上下文中,grep
不返回保留元素的列表,而是返回计数
您的grep
块是{!$args->{$k}
。当$args->{$k}
为false时,返回true,反之亦然。结果不依赖于$\uuu
,因此不会检查参数是否为允许的值之一
要查看给定值是否为允许值,您必须测试某种形式的等价性,例如
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}
声明,如果arg是标量(字符串/数字),那么这大致相当于grep
,并且允许的arrayref也只包含普通标量
然而,在v18中,智能匹配被重新标记为experimantal,行为可能很快就会改变
同时,最好还是坚持使用显式grep
等,但我们可以实现两个改进:
grep
将测试所有元素,即使已经找到匹配项。这可能是低效的。List::Util
核心模块中的first
函数的语法与grep
相同,但在第一个元素之后停止。如果块与某个值匹配,则返回该值。如果没有匹配的值,则返回undef
。当undef
可能是一个有效值时,甚至当允许使用假值时,这会使事情变得复杂。但是在您的情况下,grep
可以替换为
use List::Util 'first';
defined first { $_ eq $args->{$k} } @{ $opts->{$k}{allowed} }
MoreUtils模块具有更多的功能。例如,它提供了any
函数,该函数对应于数学表达式∃ (存在)量词:
use List::MoreUtils 'any';
any { $_ eq $args->{$k} } @{ $opts->{$k}{allowed} }
这只返回一个布尔值。虽然它可能没有普通的grep
或first
那么有效,但使用any
是非常自我记录的,并且更易于使用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
哈希中的未知选项。特别是如果考虑类的可组合性,则可能希望忽略未知选项,因为超类或子类可能需要这些选项。
但如果您选择检查错误的选项,则可以删除那些您已经处理过的元素:
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
和$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 );
但这不会产生散列。相反,=>
和,
的行为类似于C中的逗号运算符:计算并丢弃左操作数。也就是说,只保留最后一个元素:
my $args = 1; # equivalent to above snippet.
@JasonGray我查看了你的更新代码。在循环中,您应该区分arg是否存在:if(exists$args->{$k}{test\u allowed}else{assign\u default}
。当测试允许的值时,您应该首先检查是否有允许的值列表,否则您要接受所有值:if(my$allowed=$opts->{$k}->{allowed}){croak“Value…notallowed!”,除非有任何{$args->{$k}eq${}@$allowed}
。这样可以避免硬编码检查。