## `proc` -- define a procedure

#### Introduction

`proc - end_proc` defines a procedure.

#### Call(s)

(x1, x2, ...) -> body
proc(
x1 <= default1> <: type1>,
x2 <= default2> <: type2>, ...
) <: returntype>
<name pname;>
<option option1, option2, ...;>
<local local1, local2, ...;>
<save global1, global2, ...;>
begin
body
end_proc `_procdef(...)`

#### Parameters

`x1, x2, ...` | - | the formal parameters of the procedure: identifiers
`default1, default2, ...` | - | default values for the parameters: arbitrary MuPAD objects
`type1, type2, ...` | - | 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
`option1, option2, ...` | - | available options are: escape, hold, noDebug, remember
`local1, local2, ...` | - | the local variables: identifiers
`global1, global2, ...` | - | global variables: identifiers
`body` | - | the body of the procedure: an arbitrary sequence of statements

#### Returns

a procedure of type `DOM_PROC`.

#### Related Functions

`args`, `context`, `debug`, `expose`, `hold`, `MAXDEPTH`, `newDomain`, `Pref::ignoreNoDebug`, `Pref::noProcRemTab`, `Pref::typeCheck`, `Pref::warnDeadProcEnv`, `return`, `testargs`, `Type`

#### Details

- 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 f: x -> x^2. If the procedure uses more than one parameter, use brackets as in `f := (x, y) -> x^2 + y^2`. Cf. example 1.
- 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 2.

- 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 3. Checking the input parameters should be a standard feature of every procedure. Also refer to `testargs`.

  Also an automatic type checking for the return value may be implemented specifying `returntype`. Cf. example 3.

- 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 4.

- 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 5. 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. Cf. example 6.
- noDebug prevents the MuPAD source code debugger from entering this procedure. Also refer to `Pref::ignoreNoDebug`. Cf. example 7.
- 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 8. However, the remember table may grow large and use a lot of memory. Also refer to `Pref::noProcRemTab`.
- 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 9.

  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 10.

- 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 11.
- 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 12.
- The environment variable `MAXDEPTH` limits the ``nesting depth'' of recursive procedure calls. The default value is `MAXDEPTH` = 500. Cf. example 8.
- 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.
- `_procdef` is a function of the system kernel.

#### Example 1

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)

                   2             2                         2
            2 x + x  + 1, 2 y + y  + 1, 2 a + 2 b + (a + b)  + 1, 6.25

    >> f := n -> isprime(n) and isprime(n + 2):
       f(i) $ i = 11..18

               TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE

The following command maps an ``anonymous'' procedure to the elements of a list:

    >> map([1, 2, 3, 4, 5, 6], x -> x^2)

                               [1, 4, 9, 16, 25, 36]

    >> delete f:

#### Example 2

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)

                          [x, y, z], [x, y, 2], [x, 1, 2]

No default value was declared for the first argument. A warning is issued if this argument is missing:

    >> f()

          Warning: Uninitialized variable 'x' used;
          during evaluation of 'f'

                                    [NIL, 1, 2]

    >> delete f:

#### Example 3

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: Wrong type of 1. argument (type 'Type::PosInt' expected,
                 got argument '-1');
          during evaluation of '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: Wrong type of return value (type 'Type::Integer' expected,
                 value is '3/2');
          during evaluation of 'f'

    >> delete f:

#### Example 4

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)

                       f(x), f(x + 1), 0.3333333333, - 0.5 I

Also error messages use this name:

    >> f(0)

          Error: Division by zero;
          during evaluation of '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)

                  myName(x), myName(x + 1), 0.3333333333, - 0.5 I

    >> f(0)

          Error: Division by zero;
          during evaluation of 'myName'

    >> delete f:

#### Example 5

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' used;
          during evaluation of 'g'
          Error: Illegal operand [_power];
          during evaluation of 'g'

`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)

                                    3   3
                                   x , y , 1000

    >> delete f, g:

#### Example 6

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)

                          sin(x) + 2 = 3, 1/2 + 1/3 = 5/6

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)

                          sin(x) + 2 = 3, 1/2 + 1/3 = 5/6

    >> delete f, g, x:

#### Example 7

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:

#### Example 8

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)

                                  "computing 5!"

                                  "computing 10!"

                                   120, 3628800

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)

                                  "computing 15!"

                                   1308109824000

`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)

                            22698374052006863956975682

Due to the recursive nature of `f`, the arguments are restricted by the maximal recursive depth (see `MAXDEPTH`):

    >> f(1000)

          Error: Recursive definition [See ?MAXDEPTH];
          during evaluation of 'Type::testtype'

Without `option` remember, 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)

                                      317811

    >> delete f:

#### Example 9

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

                             "x, y" = (15129, 1860867)

                                   1875996, 0, 0

    >> delete f, x, y:

#### Example 10

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

                                        10

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)

                               -32.0, 0.02616403997

The value of `DIGITS` was not changed by the call to `myfloat`:

    >> DIGITS

                                        10

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)

                   1 - 2 exp(-1), 2 - 5 exp(-1), 6 - 16 exp(-1)

Because of `save x`, the previously assigned value of `x` is restored after the integration:

    >> x

                                         3

    >> delete myfloat, x, f:

#### Example 11

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)

                                      c, b, a

    >> f(1, 2, 3, 4, 5, 6, 7)

                                7, 6, 5, 4, 3, 2, 1

    >> delete f:

#### Example 12

Use `expose` to see the source code of a procedure:

    >> f := proc(x = 0, n : DOM_INT)
       begin
         sourceCode;
       end_proc

                               proc f(x, n) ... end

    >> expose(f)

                             proc(x = 0, n : DOM_INT)
                               name f;
                             begin
                               sourceCode
                             end_proc

    >> delete f:

#### Changes

- See also the chapter The new MuPAD Language of the accompanying document From MuPAD 1.4 to MuPAD 2.0.
- ``Pure functions'' of type `DOM_EXEC`, generated by `fun` and `func` in previous versions, do not exist any longer.
- Local variables of a procedure cannot be accessed from ``the outside'' any longer: the new version uses lexical scoping instead of dynamical scoping.
- Local variables of a procedure cannot be used as symbolic variables any longer. Local variables must be assigned values, before they can be used for a computation.
- In previous versions, global environment variables such as `DIGITS` could be declared as local variables if the procedure changed them locally. In the present version, this does not work any longer. Instead, the new keyword `save` was introduced.
- The new option escape was introduced.
- `end` can be used in addition to `end_proc` to close a procedure definition.

Do you have questions or comments?
Copyright © SciFace Software GmbH & Co. KG 2000