2012-03-04 14 views
5

muszę sprawdzić hashrefs jak tenPorównywanie i uwierzytelnianiu struktury danych

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

przeciwko arrayref z hashrefs wyrażając warunki jak ten

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

i zwraca wartość true, jeśli są spełnione wszystkie warunki.

Żadna z dwóch struktur danych nie została wcześniej określona. Jeden jest zbudowany z analizowania łańcucha w bazie danych, a drugi z analizy składni użytkownika.

W powyższym przypadku, chciałbym powrócić prawda, ale gdybym sprawdził hashref przeciwko

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

bym return false, ponieważ foo w pierwszym hashref nie jest < = 60.

The Pytanie brzmi: jaka jest najlepsza strategia na zrobienie tego?

Myślę

  • budowy serię subrefs poprzez eval
  • sprawdzanie przed odpowiednim jednym spośród 5 różnych gotowych subrefs (jeden na razie dla>, <, < =,> = i ==)

Czy zmierzam w złym kierunku? a jeśli nie, jakie są najlepsze, eval czy wbudowane funkcje?

Zajrzałem do Params :: Validate, ale obawiam się, że to będzie dużo kosztów ogólnych, i tak czy inaczej będę musiał budować callbacki.

+0

Tablica pojedynczych znaków krzyżyka byłaby przydatna tylko wtedy, gdy byłyby zduplikowane klucze. Na przykład. '[{foo => ...}, {foo => ...}]' Podejrzewam, że nie masz duplikatów kluczy, co powoduje, że jest to zbędne i prawdopodobnie powinieneś użyć skrótu, i pomiń tablicę. – TLP

Odpowiedz

7

Zamiast tego użyj odwołań do kodu, a będziesz mieć gotowe walidatory. Uprośniłem twoją strukturę kondycji. Nie ma potrzeby posiadania dodatkowego poziomu tablicy, chyba że masz zduplikowane klawisze mieszające, które, jak zakładam, nie masz.

Uproszczony sub { $_[0] <= 75 } po prostu porówna pierwszą wartość argumentów. Domyślnie ostatnią wartością obliczoną w podprocedurze będzie jej wartość zwracana.

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

wyjściowa:

bar : Invalid 
baz : Valid 
foo : Valid 
+0

Dzięki za odpowiedź. – simone

+0

@simone Nie ma za co. – TLP

1

Aby zbudować na odpowiedź TLP jest, można także łatwo tworzyć anonimowe kanapki z istniejących array-of-mieszań:

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

zakłada to że masz tylko jedną kontrolę dla każdego pola w oryginalnej tablicy z hasłami. Jeśli masz kilka, rzeczy stają się nieco bardziej skomplikowane, ale zawsze możesz zrobić coś takiego:

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

Ps. Jeśli ktoś się zastanawia, jak ten kod może działać, odpowiedź brzmi: closures.W szczególności, gdy te anonimowe subwoofery są tworzone wewnątrz pętli, zachowują odniesienia do zmiennych leksykalnych, nawet gdy te zmienne wykroczą poza zakres na końcu bieżącej iteracji pętli. Tak więc, na zawsze, zmienne te będą bezpiecznie odrzucane, dostępne tylko z podprogramu, który stworzyliśmy.