#####
The overarching evaluator
*Mathematica* is an interpreted language. When *Mathematica* sees an input like this:
** f[1 + 2, 3^3, x + x + 0]**
its standard procedure is to pre-evaluate the head f and the arguments ei before actually invoking the function f. Thus, by the time the code for the function f is called, it sees this:
** f[3, 9, 2 x]**
It is the overarching __evaluator__ that pre-processes the general form of expressions in this way.
The reason *Mathematica* pre-evaluates is that it's usually what you want, in order to make interactive use or programming easier. For instance, if you previously assigned a, b, and n values, you definitely want them substituted in before this the **Expand** function is called here:
And in this example, you want the **D** to compute the derivative expression before **Simplify** goes to work, since before **D** produces an expression, there's really nothing to simplify:
If Simplify acted on its literal argument, **D[ArcSin[x], {x, 8}]** , what could it try to simplify? ArcSin[x]? x? 8?? That would not make sense, so the argument evaluates first, then the outer function is called.
So, in general, before f is actually called, there are __two stages of your input__. Here they are using the generic form from earlier:
(Before)** f[1 + 2, 3^3, x + x + 0]**
(After)** f[3, 9, 2 x]**
The first stage is your original input before the evaluator gets its hands on it. The second stage has the head and arguments evaluated.
It is the second that actually calls f, so f never sees the original arguments like 1 + 2, only the reduced arguments like 3!
I usually think of the evaluator's pre-processing as applying the "evaluator function", call it eval(), to all the parts. I colored the head red and the arguments blue to emphasize them.
** **
**f**
**[**
**e1**
**, ..., **
**en**
**]**
** eval(**
**f**
**) [ eval(**
**e1**
**), ..., eval(**
**en**
**) ]**
For those of you familiar with the evaluation sequence in Appendix "A.4.1 The Standard Evaluation Sequence" of the *Mathematica* book, I'm talking about steps 2 through 4, usually called "standard argument evaluation".
Here's a contrived example to show that simple arithmetic reduces the head and argument to sensible values. Again, I've colored the head and argument, and made the function brackets large, to make it clear that this example is really of the simple form f[x], with f and x being sort of weird, complex things:
**Trace** seems like a scary function, but you can use it in this case to catch the evaluation of head and argument, and see that it results in a nice, simple **FactorInteger[108]** , which is then evaluated. I've taken the liberty of arranging the **Trace** result in a column and coloring the head and argument evaluations just as above to make it clear what each evaluation chain is.
**
** The point of this is to get you thinking of each input, and each statement in your programs, as having two stages: the original stage that you typed (or constructed), and the reduced stage where arguments have been evaluated. And to remember that the function isn't called until the second stage, so it __doesn't know____ what the original form was.__
There are cases where you really want f to receive the arguments as given by the user or the programmer, not the pre-processed, i.e. evaluated, form. Since the default behavior of the *Mathematica* language interpreter is to evaluate everything in sight until nothing more changes, you need to use non-default techniques to prevent evaluation.
Sometimes you want to send an argument into the function unevaluated. For instance, when typesetting, or analyzing some code, you might want to compute **Length** of the expression **Print[1, 2, 3]** , or use **Cases** to extract the numbers from the mixed sum **1 + 3.5 + Pi + x + (3 - I)^2** .
Sometimes you want the return value of the function to remain unevaluated until you can do something else with it.
That is the subject of this talk.
Advanced note: As you may know, in addition to evaluating head and arguments, the evaluator does other pre-processing to the original input, such as flatten **Sequence,** and invoke upvalues on arguments. These things can all alter the original input before the main function ever gets called. You might want to prevent some or all of these alterations.
| |