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

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

Dear fellow monks,

Please consider the following:

$code = 'while (<>) {'; $code .= 'if (/'; $code .= join ('/ && /', @regexen); $code .= '/) {' $code .= ' print $_;' $code .= '}} '; eval $code; die "Error: $@\n Code:\n$code\n" if ($@);

I would like to ask a specific and a general question:

Specifically, I am interested in performance: if matching many patterns, how does qr// + while-loop perform, compared with an eval of a built string containing while-loop + same patterns 'hard-coded' (as in the above example).

And more generally: how do people use eval string? Are there cases that really need it? (but I would also like to invite the more strange uses.)

I do understand that it should only be used on trusted strings.
My experience with eval string is limited, and I hope to learn from the way others are using this clearly powerful technique.

Thanks

Replies are listed 'Best First'.
Re: eval string possibilities
by davido (Cardinal) on Nov 22, 2004 at 04:22 UTC

    Performance questions such as these can be answered empirically by using Benchmark. The accompanying POD describes its use.

    The theory behind the performance difference will involve the fact that the string that you're evaling must be compiled at runtime, which will take a little time. Whether or not the time it takes is significant to your script's performance is another question. More often than not, the answer is ...not really. But you would have to profile a time-critical script to determine where the bottlenecks are before digging into how to optimize speed performance.

    Common uses for eval strings are varied. One is to build a regexp on the fly. Another is to bring back to life something that was previously dumped via Data::Dumper (though the eval is often done behind the scenes via the same module).

    And of course, any time that you find you really don't know how the code is supposed to come together until runtime string eval comes in handy (the regexp-on-the-fly is just one example).

    For the most part, you won't need string eval, except when you really need it, and in those cases, it will seem natural (though it can be tricky getting metacharacters and quoting right).

    eval is also useful, of course, for trapping errors, though often that's done using the block version of eval rather than the string version.

    There is a really good chapter on eval in Advanced Perl Programming, published by O'Reilly & Associates (known as the Cougar book). It gives a few great examples.


    Dave

      Yes, my specific question I could probably find out by testing myself, but I learned about Benchmark only a short while ago. It will probably be a little tricky to factor out things that are not related to the to-eval-or-not-eval question.

      And you are right; the little example is from the Cougar book, and my question springs from a program I started years ago based on it, and is still in use. It has grown (maybe even too much ;), and I find myself wondering what I will lose/gain by rewriting it.

      Thanks for your reply.

        I learned about Benchmark only a short while ago. It will probably be a little tricky to factor out things that are not related to the to-eval-or-not-eval question.

        In cases like this, I have learned that it's usually better to post bad code than no code at all. A small example written with Benchmark would have given repliers something to work with. They likely would have pointed out any flaws in the benchmark, and posted a modified version.

        There is a danger in this, though. Sometimes the code has small unrelated problems that derail the conversation, but I think this is a small price to pay. In my experience, posts containing pertinent code almost always get better replies.

        Sorry for the off-topic reply, but I think it will be useful for future posts. Hopefully you can avoid making some of the mistakes that I have made here at the Monastery. :-)

Re: eval string possibilities
by diotalevi (Canon) on Nov 22, 2004 at 05:38 UTC

    First see /o is dead, long live qr//! for the details on the differences between when a regular expression is compiled and sometimes re-used. Second, note that invoking the perl compiler to avoid the use of a qr// value is likely a poor choice because all compilation is complete in that case. Compilation is known to be slow and you're explicitly invoking both perl AND regexp compilation in cases that you may have been able to avoid both. Taking on extra work at runtime is rarely a good strategy for fast code.

Re: eval string possibilities
by Errto (Vicar) on Nov 22, 2004 at 04:20 UTC

    One question is whether the regexes in @regexen contain any interpolations or not. The reason it matters is that (IIRC) if they do, and you use such a regex straight-up in a while loop, then it will recompile the regex on each iteration. This is part of why you would use qr// before entering the loop. (Using /o would not be good here because if the while loop was ever entered more than once with different values of @regexen, it would sill only ever compile the patterns once).

    One other potential security/behavior issue to be aware of is that you should quotemeta() your regexes unless you intentionally want to allow them to contain regex metacharacters.

    Your general question I can't really answer. I don't know of any really useful applications of eval-string. Remember that a lot of what people use eval-string for can be accomplished with some of Perl's more esoteric but less dangerous features, such as typeglobs, symbol-table manipulation, and symbolic references.

    Update: What Zaxo said is also true, and of course you could achieve that equally easily with qr// or eval. If possible, try to have it so that the regexes that are more likely to fail appear earlier in your array.

      (Using /o would not be good here because if the while loop was ever entered more than once with different values of @regexen, it would sill only ever compile the patterns once).
      You can use //o, and if you ever intend to re-enter the loop, just do the eval over again.
Re: eval string possibilities
by eyepopslikeamosquito (Archbishop) on Nov 22, 2004 at 04:40 UTC
Re: eval string possibilities
by Zaxo (Archbishop) on Nov 22, 2004 at 04:12 UTC

    Your efficiency is improved by using the && operator. It short-circuits and returns false for the whole expression on the first mismatch. That omits many fruitless tests.

    After Compline,
    Zaxo

Re: eval string possibilities
by Arunbear (Prior) on Nov 22, 2004 at 11:49 UTC
    eval string is also useful for loading modules dynamically e.g.
    ... my $q = CGI->new; my %modules = ( simple => 'Filter::Simple', hard => 'Filter::Util::Call' ); my $filter = $q->param("filter"); if(exists $modules{$filter}) { eval "require $modules{$filter}"; } ...
    Without eval, you would have to convert the module name to a file path before passing it to require.
Re: eval string possibilities
by melora (Scribe) on Nov 23, 2004 at 01:12 UTC
    I've used eval string to good effect in a Perl script which acts as a sort of interpreter to run a program written in another language.
    I used the eval so that I could simply translate the "OR" in this other language to "||", for example, leaving it to the eval to handle the syntax. I used a hash to contain the Other Program's variables and their values, for example, and then used those variable names in building the strings for eval to use. That kind of thing.
    I was counting on the user using the Other Language's compiler (by the way, it's a compiled language) to ensure that the code was syntactically clean, before feeding it to my script.
    Using eval in this instance meant not having to delve to a needless level of detail. Yes, laziness.