2012-03-04 96 views
5

我要检查hashrefs这样一个比较和验证数据结构

{ foo => 65, bar => 20, baz => 15 } 

对hashrefs的数组引用表达这样

[ 
{ foo => { "<=" => 75 } }, 
{ bar => { "==" => 20 } }, 
{ baz => { ">=" => 5 } }, 
] 

条件,如果满足所有条件返回真值。

这两种数据结构都不是预先确定的。一个是通过解析数据库中的字符串构建的,另一个解析用户输入。

在上述情况下,我将返回true,但如果我查了hashref对

[ 
{ foo => { "<=" => 60 } }, 
{ bar => { "==" => 20 } }, 
{ baz => { ">=" => 5 } }, 
] 

我将返回false,因为FOO第一hashref不< = 60

的问题是:做这件事的最佳策略是什么?

我想到的

  • 通过EVAL
  • 检查对5种不同的预建subrefs(每箱一个用于>,<,< =,> =之中的适当的一个建造一系列subrefs的和==)

我会一起走错路吗?如果不是,什么是最好的,评估或预建功能?

我已经看过Params :: Validate,但我担心它会有很多开销,而且我必须建立回调。

+0

单个哈希引用数组只有在您有重复键时才有用。例如。 ''{{foo => ...},{foo => ...}]'我怀疑你没有重复的键,这会导致冗余,你应该使用一个散列,并跳过数组。 – TLP 2012-03-04 11:11:34

回答

7

改为使用代码引用,您将准备好使用验证器。我简化了你的条件结构。除非你有重复的散列键,否则不需要在那里有额外的数组级别,我假设你没有。

简单的sub { $_[0] <= 75 }将简单地比较参数的第一个值。默认情况下,子例程中计算出的最后一个值将是其返回值。

use v5.10; 
use strict; 
use warnings; 

my $in = { foo => 65, bar => 21, baz => 15 }; 

my $ref = { 
    foo => sub { $_[0] <= 75 } , 
    bar => sub { $_[0] == 20 } , 
    baz => sub { $_[0] >= 5 } , 
}; 

for my $key (keys %$in) { 
    if ($ref->{$key}($in->{$key})) { 
     say "$key : Valid"; 
    } else { 
     say "$key : Invalid"; 
    } 
} 

输出:

bar : Invalid 
baz : Valid 
foo : Valid 
+0

感谢您的回答。 – simone 2012-03-04 23:20:50

+0

@simone不客气。 – TLP 2012-03-04 23:26:26

1

要建立在TLP的答案,你也可以很容易地从现有的阵列的哈希值创建匿名替补:

my $array_of_hashes = [ 
    { foo => { "<=" => 75 } }, 
    { bar => { "==" => 20 } }, 
    { baz => { ">=" => 5 } }, 
]; 

my $ref = {}; 
foreach my $entry (@$array_of_hashes) { 
    my ($key, $subhash) = %$entry; 
    my ($op, $num) = %$subhash; 
    $ref->{$key} = { 
     '<=' => sub { $_[0] <= $num }, 
     '==' => sub { $_[0] == $num }, 
     '>=' => sub { $_[0] >= $num }, 
    }->{$op}; 
} 

这是假设您只有一次检查原始散列数组中的每个字段。如果你可能有几个,事情变得有点更靠谱,但你总是做这样的事情:

my $ref = {}; 
foreach my $entry (@$array_of_hashes) { 
    my ($key, $subhash) = %$entry; 
    my ($op, $num) = %$subhash; 
    my $chain = $ref->{$key} || sub {1}; 
    $ref->{$key} = { 
     '<=' => sub { $_[0] <= $num and $chain->($_[0]) }, 
     '==' => sub { $_[0] == $num and $chain->($_[0]) }, 
     '>=' => sub { $_[0] >= $num and $chain->($_[0]) }, 
    }->{$op} || $chain; 
} 

诗篇。如果有人想知道这个代码如何工作,答案是:closures。特别是,当这些匿名子被创建在循环内部时,即使在循环的当前迭代结束时这些变量超出范围之后,它们仍保留对词法变量$num$chain的引用。所以,永远之后,这些变量将会被安全地抛在一边,只能从我们创建的子程序中访问。