Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks

I have a dataset with columns and I want to evaluate a statement on each of the rows in the data. I can do this pretty easily. Loop

through each column, check if the column exists in the evaluation statement. If it does, replace the column name with the value

and do the evaluation. I can easily do this using regular expressions. Just curious how a true perl monk would approach this.

#!/usr/bin/perl use strict; use warnings; my $row = { a => 1, b => 2, c => 3, d => 4 }; my $evalStatement="a>0"; sub evaluateRow { my ($row) = @_; ## loop through each column a,b,c,d and if column exists in the evalua +tion statement ## replace column with value if (eval(1>0)) { return 1 ## "ACCEPT row" } return 0 ## "REMOVE row" }

Thank you !!

Replies are listed 'Best First'.
Re: column replacement evaluation on dataset
by choroba (Cardinal) on May 24, 2023 at 15:37 UTC
    I'm not sure what the expected output is, but I'll probably avoid using eval.
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; sub evaluate_row { my ($rules, $row) = @_; for my $column (keys %$row) { if (exists $rules->{$column}) { return 0 unless $rules->{$column}($row->{$column}); } } return 1 } sub is_greater_to_0 { $_[0] > 0 } sub is_even { 0 == $_[0] % 2 } my $rules = {a => \&is_greater_to_0, b => \&is_even}; my @rows = ({a => 1, b => 2}, {a => 0, b => 2}, {a => 1, b => 3}, {a => 0, b => 3}); for my $row (@rows) { say evaluate_row($rules, $row) ? 'ACCEPTED' : 'REJECTED'; }

    In fact, as you'll probably use the same rules for all the rows, initialising an object with the rules as the constructor argument makes even more sense. It moves us a bit farther from the initial example, but here it is:

    You can also use an object orientation system to implement the class, e.g. Moo:

    Updated: Used named subs.
    Update 2: Added the OO examples.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: column replacement evaluation on dataset
by tybalt89 (Monsignor) on May 24, 2023 at 23:13 UTC

    Two choices:

    If you are not worried about 'eval' :

    #!/usr/bin/perl use strict; use warnings; $SIG{__WARN__} = sub { die $@ }; for my $row ( { a => 1, b => 2, c => 3, d => 4 }, { a => 0, b => 2, c => 3, d => 4 }, { a => 0, b => 2, c => 2, d => 4 }, { a => 0, b => 2, c => 2, d => 3 }, ) { use Data::Dump 'dd'; dd 'row', $row; for my $test ( qw( a>0 a>1 a>2 a>3 d<=3 c>2&d>2 ) ) { use Data::Dump 'dd'; dd { $test => evaluateRow($test, $row) }; } print "\n"; } sub evaluateRow { my ($test, $row) = @_; return eval( $test =~ s(\b[a-z]+\b)( $row->{$&} // '')gier ) ? 1 : 0 +; }

    If you are worried about 'eval' :

    #!/usr/bin/perl use strict; use warnings; $SIG{__WARN__} = sub { die $@ }; for my $row ( { a => 1, b => 2, c => 3, d => 4 }, { a => 0, b => 2, c => 3, d => 4 }, { a => 0, b => 2, c => 2, d => 4 }, { a => 0, b => 2, c => 2, d => 3 }, ) { use Data::Dump 'dd'; dd 'row', $row; for my $test ( qw( a>0 a>1 a>2 a>3 d<=3 c>2&d>2 ) ) { use Data::Dump 'dd'; dd { $test => nonevalRow($test, $row) }; } print "\n"; } my $dictionary; sub error { die $_, s/\G.*//sr =~ tr/\t/ /cr, "^ $_[0] !\n" } sub want { /\G$_[1]/gc ? shift : error pop } sub nonevalRow { ( local $_, $dictionary ) = @_; $_ .= "\n"; return want(expr(0), "\n", 'Incomplete Parse'); } sub expr { /\G\h+/gc; my $value = /\G\d+/gc ? "$&" : /\G[a-z]\w*/gci ? $dictionary->{"$&"} // error("undefined variable + '$&' ") : /\G\(/gc ? want expr(0), qr/\)/, 'Missing Right Paren' : /\G\-/gc ? - expr(7) : # unary minus /\G\+/gc ? + expr(7) : # unary plus error 'Operand Expected'; $value = /\G\h+/gc ? $value : $_[0] <= 6 && /\G\*\*/gcx ? $value ** expr(6) : $_[0] <= 5 && /\G \* /gcx ? $value * expr(6) : $_[0] <= 5 && /\G \/ /gcx ? $value / expr(6) : $_[0] <= 4 && /\G \+ /gcx ? $value + expr(5) : $_[0] <= 4 && /\G \- /gcx ? $value - expr(5) : $_[0] <= 3 && /\G <=/gcx ? $value <= expr(4) ? 1 : 0 : $_[0] <= 3 && /\G >=/gcx ? $value >= expr(4) ? 1 : 0 : $_[0] <= 3 && /\G < /gcx ? $value < expr(4) ? 1 : 0 : $_[0] <= 3 && /\G > /gcx ? $value > expr(4) ? 1 : 0 : $_[0] <= 2 && /\G ==/gcx ? $value == expr(3) ? 1 : 0 : $_[0] <= 2 && /\G !=/gcx ? $value != expr(3) ? 1 : 0 : $_[0] <= 1 && /\G & /gcx ? $value & expr(2) : $_[0] <= 0 && /\G \|/gcx ? $value | expr(1) : return $value while 1; }
Re: column replacement evaluation on dataset
by Anonymous Monk on May 24, 2023 at 15:33 UTC

    this is my solution so far. it has to loop through all the columns even though only 1 needs to be replaced

    #!/usr/bin/perl use strict; use warnings; my $row = { a => 1, b => 2, c => 3, d => 4 }; my $evalStatement="a>0"; sub evaluateRow { my ($row) = @_; ## loop through each column a,b,c,d and if column exists in the evalua +tion statement ## replace column with value my $evaluateMe = $evalStatement; foreach my $colname (keys %$row) { my $val = $row->{$colname}; $evaluateMe =~ s/$colname/$val/g; } if (eval($evaluateMe)) { return 1 ## "ACCEPT row" } return 0 ## "REMOVE row" }