-1

我正在尝试将我的哈希输入与我的数据结构中的有效允许选项进行比较,如果它不是选项之一,那么我设置键的默认值。不过,我似乎在这里遗漏了一些东西。

当前数据结构的示例..

my $opts = {
     file => { require => 1 },
     head => {
               default => 1,
               allowed => [0,1],
             },
     type => { 
               default => 'foo', 
               allowed => [qw(foo bar baz)] 
             },
};

$args是我的哈希参考( 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};
   }
}
4

1 回答 1

1

检查允许值是错误的。

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等可能会更好。但我们可以实现两个改进:

  1. 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} }
    

    这仅返回一个布尔值。虽然它可能不如普通的grepor有效first,但 usingany是非常自我记录的,并且更易于使用。

  2. 到目前为止,我一直假设我们只会对允许的值进行字符串比较。这有时会起作用,但最好指定一个显式模式。例如

    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) {
  ...;
}

或者您可以遍历$argsand的组合键$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.
于 2013-07-21T11:18:00.210 回答