hold -- delay evaluation
Introductionhold(object) prevents the evaluation of
object.
Call(s)hold(object)
Parametersobject |
- | any MuPAD object |
Returnsthe unevaluated object.
Further
DocumentationChapter 5 of the MuPAD Tutorial.
Related
Functionscontext, delete, eval, freeze, genident, indexval, level, proc, val
Detailshold is intended to prevent
such an evaluation when it is undesirable.hold is when a function that
can only process numerical arguments, but not symbolical ones, is to be
plotted or to be subjected to some numerical algorithm. See
example 6.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 1 and 7.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.
hold. This has the
effect that arguments are passed to the procedure unevaluatedly. See
the help page of proc for
details.eval or
level can be used to
force a subsequent evaluation of an unevaluated object (see example 2). In procedures with option hold, use context instead.hold is a function of the system kernel.
Example
1In the following two examples, the evaluation of a
MuPAD expression is prevented using hold:
>> x := 2: hold(3*0 - 1 + 2^2 + x)
2
3 0 - 1 + 2 + x
>> hold(error("not really an error"))
error("not really an error")
Without hold, the results would be as
follows:
>> x := 2: 3*0 - 1 + 2^2 + x
5
>> 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)
0 - 1 + 4 + 2
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)
f(2)
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))
x + 1, 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)
3, 3, x + 1, x + 1
Example
2Several hold calls can be nested to prevent
subsequent evaluations:
>> x := 2: hold(x), hold(hold(x))
x, 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(%)
2, x
Another application of eval yields the value of
x:
>> eval(%)
2, 2
>> delete x, f:
Example
3The 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))
6
Example
4The function domtype evaluates its arguments:
>> x := 0: domtype(x), domtype(sin), domtype(x + 2)
DOM_INT, DOM_FUNC_ENV, DOM_INT
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))
DOM_IDENT, DOM_IDENT, DOM_EXPR
Example
5hold 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
x
2
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
x
x
However, the best way to obtain a new symbol without a
value is to use genident:
>> y := genident("x");
y
x1
x1
>> delete y:
Example
6Suppose that we want to plot the graph of the piecewise continuous function f(x) that is identically zero on the negative real axis and equal to exp(-x) on the positive real axis:
>> f := x -> if x < 0 then 0 else exp(-x) end_if:
If we pass the symbolical expression f(x)
as an argument to plotfunc2d, then an error
occurs:
>> delete x: plotfunc(f(x), x = -2..2)
Error: Can't evaluate to boolean [_less];
during evaluation of 'f'
The reason is that plotfunc2d evaluates its
arguments, and the evaluation of f(x) for a symbolical
argument x leads to an error:
>> f(x)
Error: Can't evaluate to boolean [_less];
during evaluation of 'f'
A solution is to use hold:
>> plotfunc2d(hold(f)(x), x = -2..2):
The same phenomenon occurs when we want to apply numerical integration to
f:
>> numeric::int(f(x), x = -2..2)
Error: Can't evaluate to boolean [_less];
during evaluation of 'f'
>> numeric::int(hold(f)(x), x = -2..2)
0.8646647168
Example
7The function int is unable to compute a closed form
of the following integral and returns a symbolic int call:
>> int(sin(x)*sqrt(sin(x) + 1), x)
1/2
int(sin(x) (sin(x) + 1) , x)
After the change of
variables sin(x)=t, a closed form can be computed:
>> t := time(): f := intlib::changevar(int(sin(x)*sqrt(sin(x) + 1), x), sin(x) = y); time() - t; eval(f)
/ 1/2 \
| y (y + 1) |
int| ------------, y |
| 2 1/2 |
\ (1 - y ) /
9210
1/2 / 2 y \
(y - 1) (y + 1) | --- + 4/3 |
\ 3 /
--------------------------------
2 1/2
(1 - y )
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)(sin(x)*sqrt(sin(x) + 1), x),
sin(x) = y);
time() - t;
/ 1/2 \
| y (y + 1) |
int| ------------, y |
| 2 1/2 |
\ (1 - y ) /
20