ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
5. EvalContents: One person's data is another person's program. - Programming Pearls, Communications of the ACM, Sept. 1985 Years ago, a friend of mine showed me an elegant program running on a tiny 48K machine, the BBC Micro, that accepted any mathematical expression such as Most self-respecting scripting languages such as BASIC (some versions, anyway), Perl, Tcl, LISP, and Python have a feature that clearly sets them apart from systems programming languages like C: the ability to treat character strings as little programs.[1]
For me, Perl's run-time evaluation capability is one of the biggest reasons for using the language. (The other is its terrific support for regular expressions.) I use run-time evaluation for creating little snippets of code on the fly, which then execute at typical Perl speeds (i.e., fast!), for writing sophisticated interpreters for little languages.[2] The eval function is the gateway to this power. We will use this feature in Chapter 7, Object-Oriented Programming, for creating object accessor functions, and in Chapter 11, Implementing Object Persistence, for building an SQL query evaluator, among other things. As it turns out, Perl's eval function works in two somewhat distinct ways, depending on the type of its argument. If given a string, eval treats the string as a little program and compiles and executes it (as mentioned above); this is called dynamic expression evaluation. The contents of the string may or may not be known at compile time. Alternatively, if given a block of code - that is, the code is known at compile time - eval traps run-time exceptions. Dynamic expression evaluation and exception handling are very different topics and one would expect them to be performed by different keywords. Larry Wall once mentioned that he had toyed with the idea of using a different keyword, try, for the exception-handling version, but he was into keyword conservation at that point. I find that a single keyword actually works well because expressions evaluated on the fly have a greater chance of generating run-time exceptions as code known at compile-time. In this chapter, you will gain an in-depth understanding of how the two forms of eval work and add an important dimension to your toolkit of idioms. 5.1 The String Form: Expression EvaluationWhen Perl is given a file to execute or a string as a command line option (using -e), it needs to parse the contents, check it for syntax errors, and, if all is fine, execute it. Perl makes this feature available to the programmer through the eval string form. This contrasts powerfully with languages such as C, C++, or Java, where the compiler itself is a separate beast from your program, not available to it at run-time. In other words, the Perl interpreter itself works somewhat like this: # Slurp in the entire file while ($line = <>) { $str .= $line; # Accumulate the entire file. } # $str now contains the entire file. Execute it ! eval $str; As you can see, eval handles any Perl script handed to it. The beauty of this thing is that this facility is available not just to Larry, but to mortals like you and me. Try this: # put some code inside $str $str = '$c = $a + $b'; # Perl doesn't care what's inside $str $a = 10; $b = 20; eval $str; # Treat $str as code, and execute it. print $c; # prints 30 In this snippet, $str is treated as an ordinary string at first, because that is what it is. But eval thinks of it as a program and executes it. The important point is that it doesn't think of it as a separate program, but as if it belonged right there in the original code instead of the Figure 5.1: eval compiles and executes the string in its own contextFor this reason, the string that is given to eval can use variables and subroutines available to it at that point, including my and local variables, and optionally produce new ones in the same environment. In the preceding example, the string given to eval adds two initialized variables ($a and $b) and produces a new variable, $c. If you have more than one statement inside the string (remember that the string can be as big a program as you want), eval evaluates all of them and returns the result of the last evaluation: $str = '$a++; $a + $b'; # Contains two expressions $a = 10; $b = 20; $c = eval $str; # $c gets 31 (result of the 2nd expression, $a+$b) Of course, it's quite pointless to
What if $str doesn't contain a valid Perl expression? Perl then puts an error message in a special variable called $@ (or $EVAL_ERROR, if you use the English module). Since eval compiles the string before actually executing it, this can be either a compilation or a run-time error. $@ is guaranteed to be undef if $str contains error-free code (well, I should say free of syntax errors, because it can't really protect you against flawed logic). Since eval is used by the Perl interpreter itself to parse and execute a given script, the error strings (in $@) are exactly those you see on the standard error output when processing a flawed script. There is one subtle, yet important, point that needs to be mentioned. eval treats the string as a block, which is why it is able to process a number of statements (not just expressions) and return the value of the last statement. This also means that you don't see the changes to localized or lexical variables present in the eval'ed string. |