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

Good Morning Monks. Here is my code:

#!/usr/bin/perl #because everyone says its important to include in code use warnings; use strict; use Data::Dumper; #---------------------------------------------------- #print "Enter the input file name: "; [OLD CODE] #my $filename = <STDIN>; [OLD CODE] #chomp ($filename); [OLD CODE] #---------------------------------------------------- #link to input file from command line - 2 arguments or error displayed +; Set to respective variables @ARGV == 2 or die "Invalid number or arguments. Program will now termi +nate."; my ($filter_file, $input_file) = @ARGV; ############################################### # STORE CRITERIA FOR FILTERING INPUT FILE # ############################################### open (FILTER,"$filter_file"); my @filter; <FILTER>; # read one line from the file while (<FILTER>) { # read other lines chomp; # remove "\n" from the end of the line push @filter,[(split /\t/,$_)]; # create an array of the line by +splitting it by <TAB>, make a reference of it and push the reference +to the @filter array } @filter = sort { $a->[4] <=> $b->[4] } @filter; # sort the array #--------------------------------------------------------------------- +----- #PRINTS REFERENCES TO ARRAY FROM LEAST TO GREATEST REGARDING 'ORDER' C +OLUMN #print Dumper \@filter; #print $filter[0]->[0], "\n"; #--------------------------------------------------------------------- +----- ############################################### # LOAD INPUT FILE FOR PROCESSING # ############################################### open (IN, "$input_file") or die "Cannot open file: $!"; #variables my $x=0; my @keys; my @holder; my @array_hash; my $blank = "-"; #my $threshold=.03; #my $nonsyn = "nonsynonymous"; #********************************************* #set relationship operators to variables # HOW ?!?!?!?!?!?!?! #********************************************* #open output file - Given name: OUTPUT_"input_file" or error displaye +d if file cannot be created open (OUTFILE, ">OUTPUT_$input_file") or die "Cannot create an output +file: $!"; #Column headers stored as keys; Can be called to retrieve data in spec +ified column #Hashes are put into an array; each hash is an array row; while(my $line = <IN>) { chomp($line); $x++; if ($x==1) { print OUTFILE $line, "\n"; (@keys)=split(/\t/, $line); } else { my $y=0; (@holder) = split(/\t/, $line); my %hash; for my $column (@holder) { $hash{$keys[$y]}=$column; $y++; } #************************************************************* +******* #HOW TO COMBINE PROCESSING BLANKS AND RELATIONSHIPS IN ONE STE +P?????? #************************************************************* +******* if ($hash{$filter[0]->[0]} eq $blank) { print OUTFILE $line, "\n"; } if ($hash{$filter[0]->[0]} le $filter[0]->[2]) { print OUTFILE $line, "\n"; } if ($hash{$filter[1]->[0]} eq $blank) { print OUTFILE $line, "\n"; } if ($hash{$filter[1]->[0]} eq $filter[1]->[2]) { print OUTFILE $line, "\n"; } } }

I have a tab delim file containing the following data:

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

Basically, in the beginning of the program, a filter file will be read in, and sorted through the order column and ordered from least to greatest. Each value of the filter file will be store in a variable. Then the program will read in an input file which will be processed based on the criteria defined in the filter file. Ex. the program will first read in the filter file. For column a (order #1) the user wants the program to filter through that specific column for values less than or eq to 0.3. Then the program will move on to order #2. Then the program will move on to order #3 but for this specific column, will search for anything that eq 'abc' and append it to the output file.

My question is: I can pull up the "relationship' (Ex. <=, =, etc.) for any specific column but later on in my program when I make the comparisons, how do format the variable to be used in an IF statement. Ex.

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

if the hash of column a is less than or equal to the value specified in the filter file, print line. My concern is this. I manually type "le" into the code. However, if I pulled out  $filter[0]->[1] and printed that value it would be "<=". I need to be able to use this value in the If statement. Ex.

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

How would one go about doing this? Thank you!

Replies are listed 'Best First'.
Re: How to set relational operators to variables to be used by program
by BrowserUk (Patriarch) on Jul 19, 2012 at 17:43 UTC

    I'd eval a dispatch table of operator functions into existence:

    #! perl -slw use strict; my %ops; for my $op ( qw[ < <= == >= > != lt le eq ge gt ne ] ) { $ops{ $op } = eval "sub{ \$_[0] $op \$_[1] }"; } printf "Enter criteria: "; my( $f, $op, $c ) = split ' ', <STDIN>; local $\; while( <> ) { my @f = split ','; print if $ops{ $op }->( $f[ $f ], $c ); }

    The advantages over using eval in the loop are:

    1. Only those operators you choose are executed, thus avoiding the possibility of a user supplying an "operator" like system( ... ).
    2. You only eval once for each operator; not for every line of the file.

    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: How to set relational operators to variables to be used by program
by cheekuperl (Monk) on Jul 19, 2012 at 16:13 UTC
    I just skimmed through your code and then the question at the end. It seems you wish to avail the "dynamic operator" functionality.
    Please check if the following code serves your purpose.
    $num1=10; $num2=20; for $op ("<",">","==") { if(eval "$num1 $op $num2") { print "\n$num1 $op $num2 is TRUE"; } else { print "\n$num1 $op $num2 is FALSE"; } } #---------------OUTPUT----------- 10 < 20 is TRUE 10 > 20 is FALSE 10 == 20 is FALSE
      for my $op ($filter [0]->[1]) { if (eval "$hash{$filter[0]->[0]} $op $filter[0 +]->[2]") { print OUTFILE $line, "\n"; } }

      This seems to work. Thanks! If any one else has other suggestions please inform!

        The usual warning: If you use eval, you have to make sure that either no evildoer can put any code into the file where the opcodes are stored or that you check the values with regexes so that only acceptable ops get through

        And something else: If you use $hash{$filter[0]->[0]} inside the eval string, it is substituted with its value as well. This is fine as long as you want to compare numbers, because (10 < 20) is a valid comparision expression in perl, but if you want to also compare strings, for example "blue" and "green", then the eval will fail because (blue lt green) is not a valid expression, it should be ("blue" lt "green"). Solution: Escape the operands like this: \$hash{\$filter[0]->[0]}

        And your example filter 3 (for column b) is wrong, '=' is assignement, '==' is equality (which would suggest that a regex to check for correct relationships/operators might not be a bad idea anyway)

        For that matter, you can also write:
        if (eval "$hash{$filter[0]->[0]} $filter [0]->[1] $filter[0]->[2]") { print OUTFILE $line, "\n"; }
        eliminating the extra for loop.