### 36.2 Function

·

Categories: Function definition ·Programming

#### 36.2.1 Ordinary functions

To define a function in Maxima you use the `:=` operator. E.g.

    f(x) := sin(x)

defines a function `f`. Anonymous functions may also be created using `lambda`. For example

    lambda ([i, j], ...)

can be used instead of `f` where

    f(i,j) := block ([], ...);
    map (lambda ([i], i+1), l)

would return a list with 1 added to each term.

You may also define a function with a variable number of arguments, by having a final argument which is assigned to a list of the extra arguments:

    (%i1) f ([u]) := u;
    (%o1)                      f([u]) := u
    (%i2) f (1, 2, 3, 4);
    (%o2)                     [1, 2, 3, 4]
    (%i3) f (a, b, [u]) := [a, b, u];
    (%o3)               f(a, b, [u]) := [a, b, u]
    (%i4) f (1, 2, 3, 4, 5, 6);
    (%o4)                 [1, 2, [3, 4, 5, 6]]

The right hand side of a function is an expression. Thus if you want a sequence of expressions, you do

    f(x) := (expr1, expr2, ...., exprn);

and the value of exprn is what is returned by the function.

If you wish to make a `return` from some expression inside the function then you must use `block` and `return`.

    block ([], expr1, ..., if (a > 10) then return(a), ..., exprn)

is itself an expression, and so could take the place of the right hand side of a function definition. Here it may happen that the return happens earlier than the last expression.

The first `[]` in the block, may contain a list of variables and variable assignments, such as `[a: 3, b, c: []]`, which would cause the three variables `a`,`b`,and `c` to not refer to their global values, but rather have these special values for as long as the code executes inside the `block`, or inside functions called from inside the `block`. This is called dynamic binding, since the variables last from the start of the block to the time it exits. Once you return from the `block`, or throw out of it, the old values (if any) of the variables will be restored. It is certainly a good idea to protect your variables in this way. Note that the assignments in the block variables, are done in parallel. This means, that if you had used `c: a` in the above, the value of `c` would have been the value of `a` at the time you just entered the block, but before `a` was bound. Thus doing something like

    block ([a: a], expr1, ... a: a+3, ..., exprn)

will protect the external value of `a` from being altered, but would let you access what that value was. Thus the right hand side of the assignments, is evaluated in the entering context, before any binding occurs. Using just `block ([x], ...)` would cause the `x` to have itself as value, just as if it would have if you entered a fresh Maxima session.

The actual arguments to a function are treated in exactly same way as the variables in a block. Thus in

    f(x) := (expr1, ..., exprn);

and

    f(1);

we would have a similar context for evaluation of the expressions as if we had done

    block ([x: 1], expr1, ..., exprn)

Inside functions, when the right hand side of a definition, may be computed at runtime, it is useful to use `define` and possibly `buildq`.

#### 36.2.2 Memoizing Functions

A memoizing function caches the result the first time it is called with a given argument, and returns the stored value, without recomputing it, when that same argument is given. Memoizing functions are often called array function and are in fact handled like arrays in many ways:

The names of memoizing functions are appended to the global list `arrays` (not the global list `functions`). `arrayinfo` returns the list of arguments for which there are stored values, and `listarray` returns the stored values. `dispfun` and `fundef` return the array function definition.

`arraymake` constructs an array function call, analogous to `funmake` for ordinary functions. `arrayapply` applies an array function to its arguments, analogous to `apply` for ordinary functions. There is nothing exactly analogous to `map` for array functions, although `map(lambda([x], a[x]), L)` or `makelist(a[x], x, L)`, where L is a list, are not too far off the mark.

`remarray` removes an array function definition (including any stored function values), analogous to `remfunction` for ordinary functions.

`kill(a[x])` removes the value of the array function a stored for the argument x; the next time a is called with argument x, the function value is recomputed. However, there is no way to remove all of the stored values at once, except for `kill(a)` or `remarray(a)`, which also remove the function definition.

Examples

If evaluating the function needs much time and only a limited number of points is ever evaluated (which means not much time is spent looking up results in a long list of cached results) Memoizing functions can speed up calculations considerably.

    (%i1) showtime:true$
    Evaluation took 0.0000 seconds (0.0000 elapsed) using 0 bytes.

    (%i2) a[x]:=float(sum(sin(x*t),t,1,10000));
    Evaluation took 0.0000 seconds (0.0000 elapsed) using 0 bytes.
    (%o2)        a  := float(sum(sin(x t), t, 1, 10000))
                  x

    (%i3) a[1];
    Evaluation took 5.1250 seconds (5.1260 elapsed) using 775.250 MB.
    (%o3)                   1.633891021792447

    (%i4) a[1];
    Evaluation took 0.0000 seconds (0.0000 elapsed) using 0 bytes.
    (%o4)                   1.633891021792447

As the memoizing function is only evaluated once for each input value changes in variables the memoizing function uses are not considered for values that are already cached:

    (%i1) a[x]:=b*x;
    (%o1)                       a  := b x
                                 x

    (%i2) b:1;
    (%o2)                           1

    (%i3) a[2];
    (%o3)                           2

    (%i4) b:2;
    (%o4)                           2

    (%i5) a[1];
    (%o5)                           2

    (%i6) a[2];
    (%o6)                           2