混淆与检查哈希的数组哈希

时间:2013-07-21 07:04:10

标签: perl data-structures hash hash-of-hashes

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

当前数据结构示例..

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};
   }
}

1 个答案:

答案 0 :(得分:1)

检查允许值是否有问题。

grep函数采用代码块和列表。它依次为列表中的每个元素设置$_变量。如果块返回true值,则保留该元素。在标量上下文中,grep不返回保留元素的列表,而是返回计数。

grep阻止为{!$args->{$k}}。当$args->{$k}为false时返回true,反之亦然。结果不依赖于$_,因此不会检查参数是否是允许值之一。

要查看给定值是否为允许值,您必须测试某种形式的等价,例如

if (grep { $args->{$k} eq $_ } @{ $opts->{$k}{allowed} }) {
  # this is executed when the arg matches an allowed value
} else {
  # the arg is not allowed
}

智能匹配的游览 List :: MoreUtils

如果你可以使用perl> v10,然后智能匹配可用。这将表达上述条件

use 5.010;

$args->{$k} ~~ $opts->{$k}{allowed}

lengthy table of possible type combinations表示如果arg是标量(字符串/数字),则大致相当于grep,并且允许的arrayref也只保存普通标量。

然而,智能匹配在第18版中被重新标记为实验,行为可能很快就会改变。

与此同时,坚持明确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} }
    

    这只返回一个布尔值。虽然它可能不如普通grepfirst有效,但使用any非常自我记录,并且更易于使用。

  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} };
    
  3. 防止未知选项

    您可能希望也可能不希望在$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表示未知的args:

    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.