Documentation |
Delay evaluation
This functionality does not run in MATLAB.
hold(object)
hold(object) prevents the evaluation of object.
When a MuPAD^{®} object is entered interactively, then the system evaluates it and returns the evaluated result. When a MuPAD object is passed as an argument to a procedure, then the procedure usually evaluates the argument before processing it. Evaluation means that identifiers are replaced by their values and function calls are executed. hold is intended to prevent such an evaluation when it is undesirable.
A typical application of hold is when a function that can only process numerical arguments, but not symbolical ones, is to be used as an expression. See Example 6.
Another possible reason for using hold is efficiency. For example, if a function call f(x, y) with symbolic arguments is passed as argument to another function, but is known to return itself symbolically, then the possibly costly evaluation of the "inner" function call can be avoided by passing the expression hold(f) (x, y) as argument to the "outer" function instead. Then the arguments x, y are evaluated, but the call to f is not executed. See examples Example 1 and Example 7.
Since using hold may lead to strange effects, it is recommended to use it only when absolutely necessary.
hold only delays the evaluation of an object, but cannot completely prevent it on the long run; see Example 5.
You can use freeze to completely prevent the evaluation of a procedure or a function environment.
A MuPAD procedure can be declared with the option hold. This has the effect that arguments are passed to the procedure unevaluatedly. See the help page of proc for details.
The functions eval or level can be used to force a subsequent evaluation of an unevaluated object (see example Example 2). In procedures with option hold, use context instead.
In the following two examples, the evaluation of a MuPAD expression is prevented using hold:
x := 2: hold(3*0 - 1 + 2^2 + x)
hold(error("not really an error"))
Without hold, the results would be as follows:
x := 2: 3*0 - 1 + 2^2 + x
error("not really an error")
Error: not really an error
The following command prevents the evaluation of the operation _plus, but not the evaluation of the operands:
hold(_plus)(3*0, -1, 2^2, x)
Note that in the preceding example, the arguments of the function call are evaluated, because hold is applied only to the function _plus. In the following example, the argument of the function call is evaluated, despite that fact that f has the option hold:
f := proc(a) option hold; begin return(a + 1) end_proc: x := 2: hold(f)(x)
This happens for the following reason. When f is evaluated, the option hold prevents the evaluation of the argument x of f (see the next example). However, if the evaluation of f is prevented by hold, then the option hold has no effect, and MuPAD evaluates the operands, but not the function call.
The following example shows the expected behavior:
f(x), hold(f(x))
The function eval undoes the effect of hold. Note that it yields quite different results, depending on how it is applied:
eval(f(x)), eval(hold(f)(x)), eval(hold(f(x))), eval(hold(f))(x)
Several hold calls can be nested to prevent subsequent evaluations:
x := 2: hold(x), hold(hold(x))
The result of hold ( hold(x) ) is the unevaluated operand of the outer call of hold, that is, hold(x). Applying eval evaluates the result hold(x) and yields the unevaluated identifier x:
eval(%)
Another application of eval yields the value of x:
eval(%)
delete x, f:
The following command prevents the evaluation of the operation _plus, replaces it by the operation _mult, and then evaluates the result:
eval(subsop(hold(_plus)(2, 3), 0 = _mult))
The function domtype evaluates its arguments:
x := 0: domtype(x), domtype(sin), domtype(x + 2)
Using hold, the domain type of the unevaluated objects can be determined: x and sin are identifiers, and x + 2 is an expression:
domtype(hold(x)), domtype(hold(sin)), domtype(hold(x + 2))
hold prevents only one evaluation of an object, but it does not prevent evaluation at a later time. Thus using hold to obtain a a symbol without a value is usually not a good idea:
x := 2: y := hold(x); y
In this example, deleting the value of the identifier x makes it a symbol, and using hold is not necessary:
delete x: y := x; y
However, the best way to obtain a new symbol without a value is to use genident:
y := genident("z"); y
delete y:
Consider the piecewise defined function f(x) that is identically zero on the negative real axis and equal to on the positive real axis:
f := x -> if x < 0 then 0 else exp(-x) end_if:
This function cannot be called with a symbolic argument, because the condition x < 0 cannot be decided:
f(x)
Error: Cannot evaluate to Boolean. [_less] Evaluating: f
We wish to integrate f numerically. However, the numerical integrator expects the function as an expression:
numeric::int(f(x), x = -2..2)
Error: Cannot evaluate to Boolean. [_less] Evaluating: f
The solution is to suppress premature evaluation of f when passing the function with a symbolic argument. Inside the numerical integrator, numerical values are substituted for x before the function is called and evaluated:
numeric::int(hold(f)(x), x = -2..2)
The function int is unable to compute a closed form of the following integral and returns a symbolic int call:
int(sqrt(x)*sqrt(sqrt(x) + 1), x)
After the change of variablessqrt(x)=t, a closed form can be computed:
t := time(): f := intlib::changevar(int(sqrt(x)*sqrt(sqrt(x) + 1), x), sqrt(x) = y); time() - t; eval(f)
Measuring computing times with time shows: Most of the time in the call to intlib::changevar is spent in re-evaluating the argument. This can be prevented by using hold:
t := time(): f := intlib::changevar(hold(int)(sqrt(x)*sqrt(sqrt(x) + 1), x), sqrt(x) = y); time() - t;