Documentation |
Define a procedure
This functionality does not run in MATLAB.
( x_{1}, x_{2}, … ) -> body proc( x_{1} <= default_{1}> <: type_{1}>, x_{2} <= default_{2}> <: type_{2}>, ... ) <: returntype> <name pname;> <option option_{1}, option_{2}, …> <local local_{1}, local_{2}, …> <save global_{1}, global_{2}, …> begin body end_proc ( x_{1}, x_{2}, … ) --> body _procdef(, …)
proc - end_proc defines a procedure.
Procedures f := proc(x1, x2, ...) ... end_proc may be called like a system function in the form f(x1, x2, ...). The return value of this call is the value of the last command executed in the procedure body (or the value returned by the body via the function return).
The procedure declaration (x1, x2, ...) -> body is equivalent to proc(x1, x2, ...) begin body end_proc. It is useful for defining simple procedures that do not need local variables. E.g., f := x -> x^2 defines the mathematical function . If the procedure uses more than one parameter, use brackets as in f := (x, y) -> x^2 + y^2. Cf. Example 1.
The procedure declaration (x1, x2, ...) --> body is equivalent to fp::unapply(body, x1, x2, ...). The difference from the other definitions is that body is evaluated before defining the procedure. Cf. Example 2.
Note: The evaluation of body must not contain references to parameters or local variables of an outer procedure. |
A MuPAD^{®} procedure may have an arbitrary number of parameters. For each parameter, a default value may be specified. This value is used if no actual value is passed when the procedure is called. E.g.,
f := proc(x = 42) begin body end_proc
defines the default value of the parameter x to be 42. The call f() is equivalent to f(42). Cf. Example 3.
For each parameter, a type may be specified. This invokes an automatic type checking when the procedure is called. E.g.,
f := proc(x : DOM_INT) begin body end_proc
restricts the argument x to integer values. If the procedure is called with an argument of a wrong data type, the evaluation is aborted with an error message. Cf. Example 4. Checking the input parameters should be a standard feature of every procedure. See Testing Arguments.
Also an automatic type checking for the return value may be implemented specifying returntype. Cf. Example 4.
With the keyword name, a name may be defined for the procedure, e.g.,
f := proc(...) name myName; begin body end_proc.
There is a special variable procname associated with a procedure which stores its name. When the body returns a symbolic call procname(args()), the actual name is substituted. This is the name defined by the optional name entry. If no name entry is specified, the first identifier the procedure has been assigned to is used as the name, i.e., f in this case. Cf. Example 5.
With the keyword option, special features may be specified for a procedure:
escape
Must be used if the procedure creates and returns a new procedure which accesses local values of the enclosing procedure. Cf. Example 6. This option should only be used if necessary. Also refer to Pref::warnDeadProcEnv.
hold
Prevents the procedure from evaluating the actual parameters it is called with. See Example 7.
noDebug
Prevents the MuPAD source code debugger from entering this procedure. Also refer to Pref::ignoreNoDebug. Cf. Example 8.
noFlatten
Prevents flattening of sequences passed as arguments of the procedure. See Example 9.
remember
Instructs the procedure to store each computed result in a so-called remember table. When this procedure is called later with the same input parameters, the result is read from this table and needs not be computed again.
This may speed up, e.g., recursive procedures drastically. Cf. Example 10. However, the remember table may grow large and use a lot of memory. Furthermore, the usefulness of this function is very limited in the light of properties—identification of "the same input parameters" does not depend on assumptions on identifiers or global variables such as DIGITS and ORDER, so the returned result may not be compatible with new assumptions. Use of prog::remember instead of this option is highly recommended for any function accepting symbolic input.
noExpose
Instructs MuPAD to hide the procedure body from the user. Note that this prevents debugging the procedure, too. Cf. Example 15.
With the keyword local, the local variables of the procedure are specified, e.g.,
f := proc(...) local x, y; begin body end_proc.
Cf. Example 11.
Local variables cannot be used as "symbolic variables" (identifiers). They must be assigned values before they can be used in computations.
Note that the names of global MuPAD variables such DIGITS, READPATH etc. should not be used as local variables. Also refer to the keyword save.
With the keyword save, a local context for global MuPAD variables is created, e.g.,
f := proc(...) save DIGITS; begin DIGITS := newValue; ... end_proc.
This means that the values these variables have on entering the procedure are restored on exiting the procedure. This is true even if the procedure is exited because of an error. Cf. Example 12.
One can define procedures that accept a variable number of arguments. E.g., one may declare the procedure without any formal parameters. Inside the body, the actual parameters the procedure is called with may be accessed via the function args. Cf. Example 13.
Calling a procedure name f, say, usually does not print the source code of the body to the screen. Use expose(f) to see the body. Cf. Example 14.
The environment variable MAXDEPTH limits the "nesting depth" of recursive procedure calls. The default value is MAXDEPTH = 500. Cf. Example 10.
If a procedure is a domain slot, the special variable dom contains the name of the domain the slot belongs to. If the procedure is not a domain slot, the value of dom is NIL.
Instead of end_proc, also the keyword end can be used.
The imperative declaration proc - end_proc internally results in a call of the kernel function _procdef. There is no need to call _procdef directly.
When evaluating a procedure, MuPAD parses the entire procedure first, and only then executes it. If you want to introduce a new syntax (for example, define a new operator), do it outside a procedure. See Example 16.
Simple procedures can be generated with the "arrow operator" ->:
f := x -> x^2 + 2*x + 1: f(x), f(y), f(a + b), f(1.5)
f := n -> isprime(n) and isprime(n + 2): f(i) $ i = 11..18
The following command maps an "anonymous" procedure to the elements of a list:
map([1, 2, 3, 4, 5, 6], x -> x^2)
delete f:
The declaration of procedures with the "arrow operator" is a powerful tool. In some situations, however, it results in potentially unexpected results:
f := x -> sin(x^2)
g := x -> f'(x)
The reason is simple: The body of a procedure definition is not evaluated at the time of definition. For those occasions where evaluation is desired, the long version of the arrow operator should be used:
g := x --> f'(x)
Of course, in this example, there is an even shorter way:
g := f'
The declaration of default values is demonstrated. The following procedure uses the default values if the procedure call does not provide all parameter values:
f := proc(x, y = 1, z = 2) begin [x, y, z] end_proc: f(x, y, z), f(x, y), f(x)
No default value was declared for the first argument. A warning is issued if this argument is missing:
f()
Warning: Uninitialized variable 'x' is used. Evaluating: f
delete f:
The automatic type checking of procedure arguments and return values is demonstrated. The following procedure accepts only positive integers as argument:
f := proc(n : Type::PosInt) begin n! end_proc:
An error is raised if an unsuitable parameter is passed:
f(-1)
Error: The object '-1' is incorrect. The type of argument number 1 must be 'Type::PosInt'. Evaluating: f
Error: Wrong type of 1. argument (type 'Type::PosInt' expected, got argument '-1'); Evaluating: f
In the following procedure, automatic type checking of the return value is invoked:
f := proc(n : Type::PosInt) : Type::Integer begin n/2 end_proc:
An error is raised if the return value is not an integer:
f(3)
Error: The return value '3/2' is incorrect. The type of the return value must be 'Type::Integer'. Evaluating: f
Error: Wrong type of return value (type 'Type::Integer' expected, value is '3/2'); Evaluating: f
delete f:
The name entry of procedures is demonstrated. A procedure returns a symbolic call to itself by using the variable procname that contains the current procedure name:
f := proc(x) begin if testtype(x,Type::Numeric) then return(float(1/x)) else return(procname(args())) end_if end_proc: f(x), f(x + 1), f(3), f(2*I)
Also error messages use this name:
f(0)
Error: Division by zero. [_invert] Evaluating: f
If the procedure has a name entry, this entry is used:
f := proc(x) name myName; begin if testtype(x,Type::Numeric) then return(float(1/x)) else return(procname(args())) end_if end_proc: f(x), f(x + 1), f(3), f(2*I)
f(0)
Error: Division by zero. [_invert] Evaluating: myName
delete f:
The option escape is demonstrated. This option must be used if the procedure returns another procedure that references a formal parameter or a local variable of the generating procedure:
f := proc(n) begin proc(x) begin x^n end_proc end_proc:
Without the option escape, the formal parameter n of f leaves its scope: g := f(3) references n internally. When g is called, it cannot evaluate n to the value 3 that n had inside the scope of the function f:
g := f(3): g(x)
Warning: Uninitialized variable 'unknown' is used. Evaluating: g
Error: The operand is invalid. [_power] Evaluating: g
The option escape instructs the procedure f to deal with variables escaping the local scope. Now, the procedure g := f(3) references the value 3 rather than the formal parameter n of f, and g can be executed correctly:
f := proc(n) option escape; begin proc(x) begin x^n end_proc end_proc: g := f(3): g(x), g(y), g(10)
delete f, g:
The option hold is demonstrated. With hold, the procedure sees the actual parameter in the form that was used in the procedure call. Without hold, the function only sees the value of the parameter:
f := proc(x) option hold; begin x end_proc: g := proc(x) begin x end_proc: x := PI/2: f(sin(x) + 2) = g(sin(x) + 2), f(1/2 + 1/3) = g(1/2 + 1/3)
Procedures using option hold can evaluate the arguments with the function context:
f := proc(x) option hold; begin x = context(x) end_proc: f(sin(x) + 2), f(1/2 + 1/3)
delete f, g, x:
The option noDebug is demonstrated. The debug command starts the debugger which steps inside the procedure f. After entering the debugger command c (continue), the debugger continues the evaluation:
f := proc(x) begin x end_proc: debug(f(42))
Activating debugger... #0 in f($1=42) at /tmp/debug0.556:4 mdx> c Execution completed. 42
With the option noDebug, the debugger does not step into the procedure:
f := proc(x) option noDebug; begin x end_proc: debug(f(42))
Execution completed. 42
delete f:
Create a procedure that accepts two arguments and returns a table containing the arguments:
f := proc(x, y) begin table(x = y) end_proc:
The parameters x, y of the procedure f form a sequence. If you call this procedure for the sequence (a, b) and a variable c, MuPAD flattens the nested sequence ((a, b), c) into (a, b, c). The procedure f accepts only two arguments. Thus, it uses a and b, and ignores c:
f((a, b), c)
When you use the noFlatten option, MuPAD does not flatten the arguments of the procedure:
g := proc(x, y) option noFlatten; begin table(x = y) end_proc: g((a, b), c)
For further computations, delete f and g:
delete f, g:
The option remember is demonstrated. The print command inside the following procedure indicates if the procedure body is executed:
f:= proc(n : Type::PosInt) option remember; begin print("computing ".expr2text(n)."!"); n! end_proc: f(5), f(10)
When calling the procedure again, all values that were computed before are taken from the internal "remember table" without executing the procedure body again:
f(5)*f(10) + f(15)
option remember is used in the following procedure which computes the Fibonacci numbers F(0) = 0, F(1) = 1, F(n) = F(n - 1) + F(n - 2) recursively:
f := proc(n : Type::NonNegInt) option remember; begin if n = 0 or n = 1 then return(n) end_if; f(n - 1) + f(n - 2) end_proc:
f(123)
Due to the recursive nature of f, the arguments are restricted by the maximal recursive depth (see MAXDEPTH):
f(1000)
Error: Recursive definition: the maximal depth for nested procedure calls is reached. Evaluating: f
Without optionremember, the recursion is rather slow:
f := proc(n : Type::NonNegInt) begin if n = 0 or n = 1 then return(n) end_if; f(n - 1) + f(n - 2) end_proc:
f(28)
delete f:
We demonstrate the use of local variables:
f := proc(a) local x, y; begin x := a^2; y := a^3; print("x, y" = (x, y)); x + y end_proc:
The local variables x and y do not coincide with the global variables x, y outside the procedure. The call to f does not change the global values:
x := 0: y := 0: f(123), x, y
delete f, x, y:
The save declaration is demonstrated. The following procedure changes the environment variable DIGITS internally. Because of save DIGITS, the original value of DIGITS is restored after return from the procedure:
myfloat := proc(x, digits) save DIGITS; begin DIGITS := digits; float(x); end_proc:
The current value of DIGITS is:
DIGITS
With the default setting DIGITS = 10, the following float conversion suffers from numerical cancellation. Due to the higher internal precision, myfloat produces a more accurate result:
x := 10^20*(PI - 21053343141/6701487259): float(x), myfloat(x, 20)
The value of DIGITS was not changed by the call to myfloat:
DIGITS
The following procedure needs a global identifier, because local variables cannot be used as integration variables in the int function. Internally, the global identifier x is deleted to make sure that x does not have a value:
f := proc(n) save x; begin delete x; int(x^n*exp(-x), x = 0..1) end_proc:
x := 3: f(1), f(2), f(3)
Because of save x, the previously assigned value of x is restored after the integration:
x
delete myfloat, x, f:
The following procedure accepts an arbitrary number of arguments. It accesses the actual parameters via args, puts them into a list, reverses the list via revert, and returns its arguments in reverse order:
f := proc() local arguments; begin arguments := [args()]; op(revert(arguments)) end_proc:
f(a, b, c)
f(1, 2, 3, 4, 5, 6, 7)
delete f:
Use expose to see the source code of a procedure:
f := proc(x = 0, n : DOM_INT) begin sourceCode; end_proc
expose(f)
proc(x = 0, n : DOM_INT) name f; begin sourceCode end_proc
delete f:
The option noExpose prevents users from reading the definition of a procedure:
f := proc(a) option noExpose; begin print(sin(a)); if is(a>1)=TRUE then cos(a) else cos(a + 2) end_if end_proc
f(x), f(0), f(3)
expose(f)
proc(a) name f; option noDebug, noExpose; begin /* Hidden */ end_proc
As you can see, setting option noExpose implicitly sets the option noDebug, too.
For more information on the intended use of this option, refer to the documentation of write.
When you evaluate a procedure, MuPAD parses the entire procedure, and only then executes it. Thus, you cannot define and use a new operator inside a procedure. For example, when MuPAD parses this procedure, it does not recognize the new operator <<. The reason is that the procedure is not executed yet, and therefore, the new operator is not defined:
f := proc(A, B) begin bitshiftleft := (a, b) -> a * 2^b: operator("<<", bitshiftleft, Binary, 950): C := A<<B; end_proc:
Error: Invalid input. 'expression' is expected. [line 6, col 10]
Define the operator << on the interactive level:
bitshiftleft := (a, b) -> a * 2^b: operator("<<", bitshiftleft, Binary, 950):
Now you can use << inside procedures on the interactive level:
f := proc(A, B) begin C := A<<B; end_proc: f(2, 1)
m<<n
x_{1}, x_{2}, … |
The formal parameters of the procedure: identifiers |
default_{1}, default_{2}, … |
Default values for the parameters: arbitrary MuPAD objects |
type_{1}, type_{2}, … |
Admissible types for the parameters: type objects as accepted by the function testtype |
returntype |
Admissible type for the return value: a type object as accepted by the function testtype |
pname |
The name of the procedure: an expression |
option_{1}, option_{2}, … |
Available options are: escape, hold, noDebug, noExpose, noFlatten, remember |
local_{1}, local_{2}, … |
The local variables: identifiers |
global1, global2, … |
Global variables: identifiers |
body |
The body of the procedure: an arbitrary sequence of statements |