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

I want to be able to generate an IF statement within a loop, and then execute it when the loop is finished.

I dont know how to do it.

For example, let's say I want to search for both the words cat and dog in a string. The code would look like this:

$str = "cat dog rat elephant giraffe"; $searchstr = "cat dog"; @array = split (/\s+/, $searchstr); foreach $animal (@array) { # Here I build my IF statement, which I will execute # when I have finished my loop! $execute .= "$str =~ /$animal/ && "; } #finish off the string...chop off hanging && $execute =~ s/ && //; # HOW DO I DO THIS??? # THIS OBVIOUSLY DOESNT WORK !!! if ($execute} { print "Found a DOG and a CAT!\n\n"; }
I heard about the eval function, but I dont know how to use it! Any help will be most welcome! I feel that Perl has a way of evaluating expressions that are created on the fly.

Cheers!
Robert

  • Comment on How to generate Perl statements on the fly and execute them when they are synthesized.
  • Download Code

Replies are listed 'Best First'.
Re (tilly) 1: How to generate Perl statements on the fly and execute them when they are synthesized.
by tilly (Archbishop) on Oct 17, 2001 at 20:45 UTC
    I would recommend that you avoid eval when there are alternatives for the same reason that people are taught to avoid goto. Namely the freedom which results is such that it makes your code harder to understand. Also when you use eval, you need to worry about the fact that it traps errors, so slight mistakes can turn into errors which (if you have not coded the error handling just right) you might miss.

    For instance in this case you can use some loop control to solve the actual problem. Consider:

    # Takes a string and a list of REs. Returns true or false # depending on whether all of them match. sub match_all { my $str = shift; foreach my $re (@_) { return 0 unless $str =~ /$re/; } return 1; }
    Note the my in there. That is necessary if you are working with strict.pm, which is a habit I strongly recommend. The time spent not tracking down typos makes it highly worthwhile.

    And then you would use this as follows:

    if (match_all($str, split(/\s+/, $search_str))) { print "All of them matched\n"; }
    (Note. Using /\Q$re\E/ for the match will handle strange characters in it, but would block using REs compiled with qr//. Choose, freedom or error handling?)

    Also you could achieve the same effect with nested loop and loop control:

    OUTER: { foreach my $animal (@animals) { last OUTER if $str !~ /$animal/; } print "Matched all of them!"; }
Re: How to generate Perl statements on the fly and execute them when they are synthesized.
by Masem (Monsignor) on Oct 17, 2001 at 20:52 UTC
    As noted, eval is your friend here.

    However, note that in the specific task you are trying to do , you don't need to use evals. You can do something like:

    @searchstrings = qw( cat dog ); if ( scalar grep { $str =~ $_ } @searchstrings == scalar @searchstrings ) { print "Found them all!\n"; }

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    It's not what you know, but knowing how to find it if you don't know that's important

Re: How to generate Perl statements on the fly and execute them when they are synthesized.
by Caillte (Friar) on Oct 17, 2001 at 20:25 UTC

    It does... eval....

    Eval is rather a complex command, follow the link for a full description.

    $japh->{'Caillte'} = $me;

Re: How to generate Perl statements on the fly and execute them when they are synthesized.
by earthboundmisfit (Chaplain) on Oct 17, 2001 at 20:40 UTC
    Typo here:
    if ($execute}
    
    Change the curly to a paren and it might work better :) Also, use single quotes in assigning $execute to prevent interpolation

    update: that chop doesn't do what you think it does. Try printing $execute after the substitution.....
    $execute =~ s/ && $//; works.

    update2 I gave bad advice on the single quotes. You WANT interpolation since $animal is meaningless outside the loop. DOH!

    $execute .= '$str =~ /'.$animal.'/ && ';
Re: How to generate Perl statements on the fly and execute them when they are synthesized.
by DrManhattan (Chaplain) on Oct 18, 2001 at 00:47 UTC
    This exact problem is actually covered in the panther book as an eval() example. The code is basically like yours, but the if() statement is built with a join() command:
    @patterns = qw(cat dog); $code = 'if (/'; $code .= join('/ && /', @patterns); $code .= '/) { print "Found a DOG and a CAT!\n\n"; }'; eval $code; die "Error: $@" if ($@);
    Here's another solution which probably has no practical application, but looks cool anyway:
    #!/usr/bin/perl use strict; my (@str, @search, %words, @matched_words); # Token string @str = qw(cat dog rat elephant giraffe); # List of search keys to check for @search = qw(cat dog); # Create a hash keyed with the token list. # E.g. $words{elephant} = 1; $words{rat} = 1; etc @words{@str} = (1) x @str; # Create an array with the same number of elements # as the number of search keys that were present in # the original string. @matched_words = @words{@search}; # Check to see if the number of matched words # is equal to the number of words we were searching # for if (scalar @search == scalar @matched_words) { print "Found a DOG and a CAT!\n\n"; }
    It's case-sensitive and doesn't do regular expressions, but I imagine it's pretty fast.

    -Matt

Re: How to generate Perl statements on the fly and execute them when they are synthesized.
by tommyw (Hermit) on Oct 17, 2001 at 20:48 UTC

    You've got double quotes around the entire string in the concatenation loop. Not good. $execute.='$str=~/'.$animal.'/ && ';

    You're removing the first && when you try to chop the last one. $execute =~ s/ && $//;

    Both of these can be more succinctly fixed with map and join. But anyway...

    You need to simple eval the expression: if (eval $execute) (Oh, and change the closing bracket).