http://www.perlmonks.org?node_id=982690

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

Hello all. For reference:

column relationship value filter_or_append order a <= 0.3 filter 1 b == abc append 2 c <= 0.3 filter 3

My program reads in this filter file and stores each value in an array.

if (eval "$hash{$filter[0]->[0]} $filter[0]->[1] $filter[0]->[ +2]") { if (eval $hash{$filter[1]->[0]} $filter[1]->[1] $filter[1] +->[2]) { if (eval "$hash{$filter[2]->[0]} $filter[2]->[1] $filt +er[2]->[2]") { print OUTFILE $line, "\n"; } } }

Whats going on: If the hash of column a evaluates to less than or equal to the value specified in the filter file (.03), continue. This part works. Then, if the hash of column b evaluates to 'equal' (== or eq; I have tried both meaning I have changed the values for column b in the filter file to both '==' and 'eq' and tried to run the program that way but no luck) the value specified in the filter file (abc), continue. This part does not work - Any comparison/evaluations of numerical values works and I can print the results to an output file. However, when I try to do this same comparison/evaluation with strings, wrong output (if any) is printed to the output file. What is a solution to compare/evaluate a string with the code I have already written?

Thank you!

Replies are listed 'Best First'.
Re: Problem when comparing strings as opposed to numerical values
by BrowserUk (Patriarch) on Jul 19, 2012 at 20:26 UTC

    That is easily dealt with using the dispatch table method. Here are a few examples:

    C:\test>dynop Enter criteria: 0 > xray zpepocpu 325 82.7545166015625 zgvappww 187 85.540771484375 zoedyjzb 780 75.0732421875 zapvcywd 987 71.014404296875 yasxnblr 328 93.7652587890625 zsqavfzn 777 28.8421630859375 yfxbhnqz 560 69.0948486328125 Enter criteria: 2 > 99.0 ahfijvsa 492 99.3072509765625 kpfaqzzz 419 99.8504638671875 Enter criteria: 0 =~ mid phmidnpi 931 60.980224609375 Enter criteria: 1 >= 900 zapvcywd 987 71.014404296875 phmidnpi 931 60.980224609375 fcaabsvp 907 62.908935546875 qqnmneah 987 27.685546875 foyqaqgx 901 51.214599609375 Enter criteria: 0 lt b ahfijvsa 492 99.3072509765625 abnualfk 166 57.2265625 axarlwit 430 82.5531005859375

    And the test code:


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re: Problem when comparing strings as opposed to numerical values
by ig (Vicar) on Jul 19, 2012 at 20:50 UTC

    I wonder if you are missing quotes in your second if.

    I'm guessing that you also need to put quotes around your strings in what I'm guessing you want to be a string eval (the second if). The string you want to evaluate should probably be something like "'string' eq 'string'".

    It might help you to examine what you are evaluating. You might use the debugger but I would probably use print or warn myself. Something like:

    if (eval "$hash{$filter[0]->[0]} $filter[0]->[1] $filter[0]->[ +2]") { print "eval \"$hash{$filter[1]->[0]} $filter[1]->[1] $filt +er[1]->[2]\""; if (eval "$hash{$filter[1]->[0]} $filter[1]->[1] $filter[1 +]->[2]") { if (eval "$hash{$filter[2]->[0]} $filter[2]->[1] $filt +er[2]->[2]") { print OUTFILE $line, "\n"; } } }

    When you see what you are evaluating, the problem will likely become evident.

    FWIW: I don't think I would use eval as you are trying to. I would probably do something more like the follwing:

    sub match { my ($hash, $filter) = @_; if($filter->[1] eq '<=') { return( $hash{$filter->[0]} <= $filter->[2] ); } elsif($filter->[1] eq 'eq') { return( $hash{$filter->[0]} eq $filter->[2] ); } else { die "unsupported comparator: $filter->[$index]->[1]"; } } if( match($hash, $filters->[0]) and match($hash, $filters->[1]) and match($hash, $filters->[2]) ) { print OUTFILE $line, "\n"; }

    If I was going to use eval, I would definitely check for errors after each eval. I suspect if you did so, you would find that the eval in your second if gives a compile error - but it's only a guess. If you did add error handling to your implementation with eval, I would find it even harder to read than what you have currently, which is already too hard for my taste, but that's just me. In any case, if you want to know why your program isn't doing what you expect, a very good place to start is error handling and reporting. To this end, you might find strict and warnings helpful.

      adding in the single quotes seemed to have fixed the problem. I appreciate the advice and am going to try and implement the 'sub match' code you have provided. Thank you!

Re: Problem when comparing strings as opposed to numerical values
by aitap (Curate) on Jul 19, 2012 at 20:16 UTC

    Firstly, there is no need in nested if's:

    if ((eval "$hash{$filter[0]->[0]} $filter[0]->[1] $filter[0]->[2]") && + (eval $hash{$filter[1]->[0]} $filter[1]->[1] $filter[1]->[2]) && (eval "$hash{$filter[2]->[0]} $filter[2]->[1] $filter[2]->[2]")) { print OUTFILE $line, "\n"; }

    Secondly, you have quotes (") missing in the second statement. This can be the source of the problem. You can try using other quoting operators.

    Thirdly, it can be safer to write a specific function to perform any needed checks on your data:

    sub check { my ($a, $check, $b) = @_; if ($check eq "==") { return $a == $b; } elsif ($check eq ">=") { return $a >= $b; } elsif ($check eq "<=") { return $a <= $b; } elsif ($check eq "eq") { return $a eq $b; } else { die "Unimplemented check $check" } } if (&check($hash{$filter[0]->[0]},$filter[0]->[1],$filter[0]->[2]) && +&check(...) && &check(...)) { print OUTFILE $line; }
    Some syntax problems caused by special characters inside the strings will be also avoided by this method.

    Sorry if my advice was wrong.

      I tried it with the quotes but theres no output to the file.

      I also tried:

       (eval /$hash{/$filter[1]->[0]} $filter[1]->[1] $filter[1]->[2])

      that didnt work either. It only works when I manually type in 'eq' - Example:

       (eval $hash{$filter[1]->[0]} eq $filter[1]->[2])

      But my whole point is to use the reference to the array to pull the variable out which should be 'eq' in this case. I am stumped. Any ideas on what to do?

        What if you print what you are trying to eval? I mean, run perl -d <your script>.pl, set a breakpoint on the line where the string comparsion occurs (b <line number>), run program to that line (c) and enter: x "$hash{$filter[1]->[0]} $filter[1]->[1] $filter[1]->[2]". What will it print?

        Maybe using eval "q{$hash{$filter[1]->[0]}} $filter[1]->[1] q{$filter[1]->[2]}" can help, but it will break too if the strings contain some "{" or "}". What if you check for $@ special variable after running your evals?

        Are you sure it won't be easier and safer to write a special sub to perform the checking?

        Sorry if my advice was wrong.
Re: Problem when comparing strings as opposed to numerical values
by Anonymous Monk on Jul 19, 2012 at 19:15 UTC

    What are you trying to accomplish with those evals?

    Have you considered what will happen if your input contains a string value along the lines of "};  system('format c:'); #"

      Yes. Later on, I am going to add regexes to check the values.