Next: Debugging, Previous: Function Definition [Contents][Index]
Next: Garbage Collection, Previous: Program Flow, Up: Program Flow [Contents][Index]
Maxima is a fairly complete programming language. But since it is written in
Lisp, it additionally can provide easy access to Lisp functions and variables
from Maxima and vice versa. Lisp and Maxima symbols are distinguished by a
naming convention. A Lisp symbol which begins with a dollar sign $
corresponds to a Maxima symbol without the dollar sign.
A Maxima symbol which begins with a question mark ?
corresponds to a Lisp
symbol without the question mark. For example, the Maxima symbol foo
corresponds to the Lisp symbol $FOO
, while the Maxima symbol ?foo
corresponds to the Lisp symbol FOO
. Note that ?foo
is written
without a space between ?
and foo
; otherwise it might be mistaken
for describe ("foo")
.
Hyphen -
, asterisk *
, or other special characters in Lisp symbols
must be escaped by backslash \
where they appear in Maxima code. For
example, the Lisp identifier *foo-bar*
is written ?\*foo\-bar\*
in Maxima.
Lisp code may be executed from within a Maxima session. A single line of Lisp
(containing one or more forms) may be executed by the special command
:lisp
. For example,
(%i1) :lisp (foo $x $y)
calls the Lisp function foo
with Maxima variables x
and y
as arguments. The :lisp
construct can appear at the interactive prompt
or in a file processed by batch
or demo
, but not in a file
processed by load
, batchload
,
translate_file
, or compile_file
.
The function to_lisp
opens an interactive Lisp session.
Entering (to-maxima)
closes the Lisp session and returns to Maxima.
Lisp functions and variables which are to be visible in Maxima as functions and
variables with ordinary names (no special punctuation) must have Lisp names
beginning with the dollar sign $
.
Maxima is case-sensitive, distinguishing between lowercase and uppercase letters in identifiers. There are some rules governing the translation of names between Lisp and Maxima.
$foo
, $FOO
, and $Foo
all correspond to Maxima foo
. But this is because $foo
,
$FOO
and $Foo
are converted by the Lisp reader by default to the
Lisp symbol $FOO
.
|$FOO|
and |$foo|
correspond to Maxima foo
and FOO
,
respectively.
|$Foo|
corresponds to Maxima Foo
.
The #$
Lisp macro allows the use of Maxima expressions in Lisp code.
#$expr$
expands to a Lisp expression equivalent to the Maxima
expression expr.
(msetq $foo #$[x, y]$)
This has the same effect as entering
(%i1) foo: [x, y];
The Lisp function displa
prints an expression in Maxima format.
(%i1) :lisp #$[x, y, z]$ ((MLIST SIMP) $X $Y $Z) (%i1) :lisp (displa '((MLIST SIMP) $X $Y $Z)) [x, y, z] NIL
Functions defined in Maxima are not ordinary Lisp functions. The Lisp function
mfuncall
calls a Maxima function. For example:
(%i1) foo(x,y) := x*y$ (%i2) :lisp (mfuncall '$foo 'a 'b) ((MTIMES SIMP) A B)
Some Lisp functions are shadowed in the Maxima package, namely the following.
complement continue // float functionp array exp listen signum atan asin acos asinh acosh atanh tanh cosh sinh tan break gcd
Next: Introduction to Program Flow, Previous: Lisp and Maxima, Up: Program Flow [Contents][Index]
One of the advantages of using lisp is that it uses “Garbage Collection”. In other words it automatically takes care of freeing memory occupied for example of intermediate results that were used during symbolic computation.
Garbage Collection avoids many errors frequently found in C programs (memory being freed too early, multiple times or not at all).
Tries to manually trigger the lisp’s garbage collection. This rarely is necessary as the lisp will employ an excellent algorithm for determining when to start garbage collection.
If maxima knows how to do manually trigger the garbage collection for the
current lisp garbage_collect
returns true
, else false
.
Next: Functions and Variables for Program Flow, Previous: Garbage Collection, Up: Program Flow [Contents][Index]
Maxima provides a do
loop for iteration, as well as more primitive
constructs such as go
.
Previous: Introduction to Program Flow, Up: Program Flow [Contents][Index]
Prints the call stack, that is, the list of functions which called the currently active function.
backtrace ()
prints the entire call stack.
backtrace (n)
prints the n most recent
functions, including the currently active function.
backtrace
can be called from a script, a function, or the interactive
prompt (not only in a debugging context).
Examples:
backtrace ()
prints the entire call stack.
(%i1) h(x) := g(x/7)$ (%i2) g(x) := f(x-11)$ (%i3) f(x) := e(x^2)$ (%i4) e(x) := (backtrace(), 2*x + 13)$ (%i5) h(10); #0: e(x=4489/49) #1: f(x=-67/7) #2: g(x=10/7) #3: h(x=10) 9615 (%o5) ---- 49
backtrace (n)
prints the n most recent
functions, including the currently active function.
(%i1) h(x) := (backtrace(1), g(x/7))$ (%i2) g(x) := (backtrace(1), f(x-11))$ (%i3) f(x) := (backtrace(1), e(x^2))$ (%i4) e(x) := (backtrace(1), 2*x + 13)$ (%i5) h(10); #0: h(x=10) #0: g(x=10/7) #0: f(x=-67/7) #0: e(x=4489/49) 9615 (%o5) ---- 49
The do
statement is used for performing iteration. The general
form of the do
statements maxima supports is:
for variable: initial_value step increment
thru limit do body
for variable: initial_value step increment
while condition do body
for variable: initial_value step increment
unless condition do body
for variable in list do body
If the loop is expected to generate a list as output the command
makelist
may be the appropriate command to use instead,
See Performance considerations for Lists.
initial_value, increment, limit, and body can be any
expression. list is a list. If the increment is 1 then "step 1
"
may be omitted; As always, if body
needs to contain more than one command
these commands can be specified as a comma-separated list surrounded
by parenthesis or as a block
.
Due to its great generality the do
statement will be described in two parts.
The first form of the do
statement (which is shown in the first three
items above) is analogous to that used in
several other programming languages (Fortran, Algol, PL/I, etc.); then
the other features will be mentioned.
The execution of the do
statement proceeds by first assigning
the initial_value to the variable (henceforth called the
control-variable). Then: (1) If the control-variable has exceeded the
limit of a thru
specification, or if the condition of the
unless
is true
, or if the condition of the while
is false
then the do
terminates. (2) The body is
evaluated. (3) The increment is added to the control-variable. The
process from (1) to (3) is performed repeatedly until the termination
condition is satisfied. One may also give several termination
conditions in which case the do
terminates when any of them is
satisfied.
In general the thru
test is satisfied when the control-variable
is greater than the limit if the increment was
non-negative, or when the control-variable is less than the
limit if the increment was negative. The
increment and limit may be non-numeric expressions as
long as this inequality can be determined. However, unless the
increment is syntactically negative (e.g. is a negative number)
at the time the do
statement is input, Maxima assumes it will
be positive when the do
is executed. If it is not positive,
then the do
may not terminate properly.
Note that the limit, increment, and termination condition are
evaluated each time through the loop. Thus if any of these involve
much computation, and yield a result that does not change during all
the executions of the body, then it is more efficient to set a
variable to their value prior to the do
and use this variable in the
do
form.
The value normally returned by a do
statement is the atom
done
. However, the function return
may be used inside
the body to exit the do
prematurely and give it any
desired value. Note however that a return
within a do
that occurs in a block
will exit only the do
and not the
block
. Note also that the go
function may not be used
to exit from a do
into a surrounding block
.
The control-variable is always local to the do
and thus any
variable may be used without affecting the value of a variable with
the same name outside of the do
. The control-variable is unbound
after the do
terminates.
(%i1) for a:-3 thru 26 step 7 do display(a)$ a = - 3 a = 4 a = 11 a = 18 a = 25
(%i1) s: 0$ (%i2) for i: 1 while i <= 10 do s: s+i; (%o2) done (%i3) s; (%o3) 55
Note that the condition while i <= 10
is equivalent to unless i > 10
and also thru 10
.
(%i1) series: 1$ (%i2) term: exp (sin (x))$ (%i3) for p: 1 unless p > 7 do (term: diff (term, x)/p, series: series + subst (x=0, term)*x^p)$ (%i4) series; 7 6 5 4 2 x x x x x (%o4) -- - --- - -- - -- + -- + x + 1 90 240 15 8 2
which gives 8 terms of the Taylor series for e^sin(x)
.
(%i1) poly: 0$ (%i2) for i: 1 thru 5 do for j: i step -1 thru 1 do poly: poly + i*x^j$ (%i3) poly; 5 4 3 2 (%o3) 5 x + 9 x + 12 x + 14 x + 15 x (%i4) guess: -3.0$ (%i5) for i: 1 thru 10 do (guess: subst (guess, x, 0.5*(x + 10/x)), if abs (guess^2 - 10) < 0.00005 then return (guess)); (%o5) - 3.162280701754386
This example computes the negative square root of 10 using the
Newton- Raphson iteration a maximum of 10 times. Had the convergence
criterion not been met the value returned would have been done
.
Instead of always adding a quantity to the control-variable one
may sometimes wish to change it in some other way for each iteration.
In this case one may use next expression
instead of
step increment
. This will cause the control-variable to be set to
the result of evaluating expression each time through the loop.
(%i6) for count: 2 next 3*count thru 20 do display (count)$ count = 2 count = 6 count = 18
As an alternative to for variable: value ...do...
the syntax for variable from value ...do...
may be
used. This permits the from value
to be placed after the
step
or next
value or after the termination condition.
If from value
is omitted then 1 is used as the initial
value.
Sometimes one may be interested in performing an iteration where the control-variable is never actually used. It is thus permissible to give only the termination conditions omitting the initialization and updating information as in the following example to compute the square-root of 5 using a poor initial guess.
(%i1) x: 1000$ (%i2) thru 20 do x: 0.5*(x + 5.0/x)$ (%i3) x; (%o3) 2.23606797749979 (%i4) sqrt(5), numer; (%o4) 2.23606797749979
If it is desired one may even omit the termination conditions entirely
and just give do body
which will continue to evaluate the
body indefinitely. In this case the function return
should be used to terminate execution of the do
.
(%i1) newton (f, x):= ([y, df, dfx], df: diff (f ('x), 'x), do (y: ev(df), x: x - f(x)/y, if abs (f (x)) < 5e-6 then return (x)))$ (%i2) sqr (x) := x^2 - 5.0$ (%i3) newton (sqr, 1000); (%o3) 2.236068027062195
(Note that return
, when executed, causes the current value of x
to
be returned as the value of the do
. The block
is exited and this
value of the do
is returned as the value of the block
because the
do
is the last statement in the block.)
One other form of the do
is available in Maxima. The syntax is:
for variable in list end_tests do body
The elements of list are any expressions which will successively
be assigned to the variable
on each iteration of the
body. The optional termination tests end_tests can be
used to terminate execution of the do
; otherwise it will
terminate when the list is exhausted or when a return
is
executed in the body. (In fact, list
may be any
non-atomic expression, and successive parts are taken.)
(%i1) for f in [log, rho, atan] do ldisp(f(1))$ (%t1) 0 (%t2) rho(1) %pi (%t3) --- 4 (%i4) ev(%t3,numer); (%o4) 0.78539816
Evaluates expr_1, …, expr_n one by one and
returns [expr_n]
(a list) if no error occurs. If an
error occurs in the evaluation of any argument, errcatch
prevents the error from propagating and
returns the empty list []
without evaluating any more arguments.
errcatch
is useful in batch
files where one suspects an error might occur which
would terminate the batch
if the error weren’t caught.
See also errormsg
.
Evaluates and prints expr_1, …, expr_n,
and then causes an error return to top level Maxima
or to the nearest enclosing errcatch
.
The variable error
is set to a list describing the error.
The first element of error
is a format string, which merges all the
strings among the arguments expr_1, …, expr_n,
and the remaining elements are the values of any non-string arguments.
errormsg()
formats and prints error
.
This is effectively reprinting the most recent error message.
Evaluates and prints expr_1, …, expr_n, as a warning message that is formatted in a standard way so a maxima front-end may be able to recognize the warning and to format it accordingly.
The function warning
always returns false.
Default value: 60
error_size
modifies error messages according to the size of expressions
which appear in them. If the size of an expression (as determined by the Lisp
function ERROR-SIZE
) is greater than error_size
, the expression is
replaced in the message by a symbol, and the symbol is assigned the expression.
The symbols are taken from the list error_syms
.
Otherwise, the expression is smaller than error_size
, and the expression
is displayed in the message.
See also error
and error_syms
.
Example:
The size of U
, as determined by ERROR-SIZE
, is 24.
(%i1) U: (C^D^E + B + A)/(cos(X-1) + 1)$ (%i2) error_size: 20$ (%i3) error ("Example expression is", U); Example expression is errexp1 -- an error. Quitting. To debug this try debugmode(true); (%i4) errexp1; E D C + B + A (%o4) -------------- cos(X - 1) + 1 (%i5) error_size: 30$ (%i6) error ("Example expression is", U); E D C + B + A Example expression is -------------- cos(X - 1) + 1 -- an error. Quitting. To debug this try debugmode(true);
Default value: [errexp1, errexp2, errexp3]
In error messages, expressions larger than error_size
are replaced by
symbols, and the symbols are set to the expressions. The symbols are taken from
the list error_syms
. The first too-large expression is replaced by
error_syms[1]
, the second by error_syms[2]
, and so on.
If there are more too-large expressions than there are elements of
error_syms
, symbols are constructed automatically, with the n-th
symbol equivalent to concat ('errexp, n)
.
See also error
and error_size
.
Reprints the most recent error message.
The variable error
holds the message,
and errormsg
formats and prints it.
Default value: true
When false
the output of error messages is suppressed.
The option variable errormsg
can not be set in a block to a local
value. The global value of errormsg
is always present.
(%i1) errormsg; (%o1) true
(%i2) sin(a,b); sin: wrong number of arguments. -- an error. To debug this try: debugmode(true);
(%i3) errormsg: false; (%o3) false
(%i4) sin(a,b); -- an error. To debug this try: debugmode(true);
The option variable errormsg
can not be set in a block to a local value.
(%i1) f(bool):=block([errormsg:bool], print ("value of errormsg is",errormsg))$
(%i2) errormsg:true; (%o2) true
(%i3) f(false); value of errormsg is true (%o3) true
(%i4) errormsg:false; (%o4) false
(%i5) f(true); value of errormsg is false (%o5) false
is used within a block
to transfer control to the statement
of the block which is tagged with the argument to go
. To tag a
statement, precede it by an atomic argument as another statement in
the block
. For example:
block ([x], x:1, loop, x+1, ..., go(loop), ...)
The argument to go
must be the name of a tag appearing in the same
block
. One cannot use go
to transfer to tag in a block
other than the one containing the go
.
Represents conditional evaluation. Various forms of if
expressions are
recognized.
if cond_1 then expr_1 else expr_0
evaluates to expr_1 if cond_1 evaluates to true
,
otherwise the expression evaluates to expr_0.
The command if cond_1 then expr_1 elseif cond_2 then
expr_2 elseif ... else expr_0
evaluates to expr_k if
cond_k is true
and all preceding conditions are false
. If
none of the conditions are true
, the expression evaluates to
expr_0
.
A trailing else false
is assumed if else
is missing. That is,
the command if cond_1 then expr_1
is equivalent to
if cond_1 then expr_1 else false
, and the command
if cond_1 then expr_1 elseif ... elseif cond_n then
expr_n
is equivalent to if cond_1 then expr_1 elseif
... elseif cond_n then expr_n else false
.
The alternatives expr_0, …, expr_n may be any Maxima
expressions, including nested if
expressions. The alternatives are
neither simplified nor evaluated unless the corresponding condition is
true
.
The conditions cond_1, …, cond_n are expressions which
potentially or actually evaluate to true
or false
.
When a condition does not actually evaluate to true
or false
,
the behavior of if
is governed by the global flag prederror
.
When prederror
is true
, it is an error if any evaluated condition
does not evaluate to true
or false
. Otherwise, conditions which
do not evaluate to true
or false
are accepted, and the result is
a conditional expression.
Among other elements, conditions may comprise relational and logical operators as follows.
Operation Symbol Type less than <
relational infix less than or equal to <=
relational infix equality (syntactic) =
relational infix equality (value) equal
relational function negation of equal notequal
relational function greater than or equal to >=
relational infix greater than >
relational infix and and
logical infix or or
logical infix not not
logical infix
Returns an expression whose leading operator is the same as that of the
expressions expr_1, …, expr_n but whose subparts are the
results of applying f to the corresponding subparts of the expressions.
f is either the name of a function of n arguments or is a
lambda
form of n arguments.
maperror
- if false
will cause all of the mapping
functions to (1) stop when they finish going down the shortest
expr_i if not all of the expr_i are of the same length and
(2) apply f to [expr_1, expr_2, …] if the
expr_i are not all the same type of object. If maperror
is true
then an error message will be given in the above two
instances.
One of the uses of this function is to map
a function (e.g.
partfrac
) onto each term of a very large expression where it ordinarily
wouldn’t be possible to use the function on the entire expression due to an
exhaustion of list storage space in the course of the computation.
See also scanmap
, maplist
, outermap
, matrixmap
and apply
.
(%i1) map(f,x+a*y+b*z); (%o1) f(b z) + f(a y) + f(x) (%i2) map(lambda([u],partfrac(u,x)),x+1/(x^3+4*x^2+5*x+2)); 1 1 1 (%o2) ----- - ----- + -------- + x x + 2 x + 1 2 (x + 1) (%i3) map(ratsimp, x/(x^2+x)+(y^2+y)/y); 1 (%o3) y + ----- + 1 x + 1 (%i4) map("=",[a,b],[-0.5,3]); (%o4) [a = - 0.5, b = 3]
Returns true
if and only if expr is treated by the mapping
routines as an atom. "Mapatoms" are atoms, numbers
(including rational numbers), subscripted variables and structure
references.
Default value: true
When maperror
is false
, causes all of the mapping functions,
for example
map (f, expr_1, expr_2, ...)
to (1) stop when they finish going down the shortest expr_i if not all of the expr_i are of the same length and (2) apply f to [expr_1, expr_2, …] if the expr_i are not all the same type of object.
If maperror
is true
then an error message
is displayed in the above two instances.
Default value: true
When mapprint
is true
, various information messages from
map
, maplist
, and fullmap
are produced in certain
situations. These include situations where map
would use
apply
, or map
is truncating on the shortest list.
If mapprint
is false
, these messages are suppressed.
Returns a list of the applications of f to the parts of the expressions expr_1, …, expr_n. f is the name of a function, or a lambda expression.
maplist
differs from map(f, expr_1, ..., expr_n)
which returns an expression with the same main operator as expr_i has
(except for simplifications and the case where map
does an apply
).
Default value: false
When prederror
is true
, an error message is displayed whenever the
predicate of an if
statement or an is
function fails to evaluate
to either true
or false
.
If false
, unknown
is returned
instead in this case. The prederror: false
mode is not supported in
translated code;
however, maybe
is supported in translated code.
May be used to exit explicitly from the current block
, while
,
for
or do
loop bringing its argument. It therefore can be compared
with the return
statement found in other programming languages but it yields
one difference: In maxima only returns from the current block, not from the entire
function it was called in. In this aspect it more closely resembles the break
statement from C.
(%i1) for i:1 thru 10 do o:i; (%o1) done
(%i2) for i:1 thru 10 do if i=3 then return(i); (%o2) 3
(%i3) for i:1 thru 10 do ( block([i], i:3, return(i) ), return(8) ); (%o3) 8
(%i4) block([i], i:4, block([o], o:5, return(o) ), return(i), return(10) ); (%o4) 4
See also for
, while
, do
and block
.
Recursively applies f to expr, in a top down manner. This is most useful when complete factorization is desired, for example:
(%i1) exp:(a^2+2*a+1)*y + x^2$ (%i2) scanmap(factor,exp); 2 2 (%o2) (a + 1) y + x
Note the way in which scanmap
applies the given function
factor
to the constituent subexpressions of expr; if
another form of expr is presented to scanmap
then the
result may be different. Thus, %o2
is not recovered when
scanmap
is applied to the expanded form of exp
:
(%i3) scanmap(factor,expand(exp)); 2 2 (%o3) a y + 2 a y + y + x
Here is another example of the way in which scanmap
recursively
applies a given function to all subexpressions, including exponents:
(%i4) expr : u*v^(a*x+b) + c$ (%i5) scanmap('f, expr); f(f(f(a) f(x)) + f(b)) (%o5) f(f(f(u) f(f(v) )) + f(c))
scanmap (f, expr, bottomup)
applies f to expr in a
bottom-up manner. E.g., for undefined f
,
scanmap(f,a*x+b) -> f(a*x+b) -> f(f(a*x)+f(b)) -> f(f(f(a)*f(x))+f(b)) scanmap(f,a*x+b,bottomup) -> f(a)*f(x)+f(b) -> f(f(a)*f(x))+f(b) -> f(f(f(a)*f(x))+f(b))
In this case, you get the same answer both ways.
Evaluates expr and throws the value back to the most recent
catch
. throw
is used with catch
as a nonlocal return
mechanism.
Applies the function f to each one of the elements of the outer product a_1 cross a_2 … cross a_n.
f is the name of a function of n arguments or a lambda expression of n arguments. Each argument a_k may be a list or nested list, or a matrix, or any other kind of expression.
The outermap
return value is a nested structure. Let x be the
return value. Then x has the same structure as the first list, nested
list, or matrix argument, x[i_1]...[i_m]
has the same structure as
the second list, nested list, or matrix argument,
x[i_1]...[i_m][j_1]...[j_n]
has the same structure as the third
list, nested list, or matrix argument, and so on, where m, n,
… are the numbers of indices required to access the elements of each
argument (one for a list, two for a matrix, one or more for a nested list).
Arguments which are not lists or matrices have no effect on the structure of
the return value.
Note that the effect of outermap
is different from that of applying
f to each one of the elements of the outer product returned by
cartesian_product
. outermap
preserves the structure of the
arguments in the return value, while cartesian_product
does not.
outermap
evaluates its arguments.
See also map
, maplist
, and apply
.
Examples:
Elementary examples of outermap
.
To show the argument combinations more clearly, F
is left undefined.
(%i1) outermap (F, [a, b, c], [1, 2, 3]); (%o1) [[F(a, 1), F(a, 2), F(a, 3)], [F(b, 1), F(b, 2), F(b, 3)], [F(c, 1), F(c, 2), F(c, 3)]]
(%i2) outermap (F, matrix ([a, b], [c, d]), matrix ([1, 2], [3, 4])); [ [ F(a, 1) F(a, 2) ] [ F(b, 1) F(b, 2) ] ] [ [ ] [ ] ] [ [ F(a, 3) F(a, 4) ] [ F(b, 3) F(b, 4) ] ] (%o2) [ ] [ [ F(c, 1) F(c, 2) ] [ F(d, 1) F(d, 2) ] ] [ [ ] [ ] ] [ [ F(c, 3) F(c, 4) ] [ F(d, 3) F(d, 4) ] ]
(%i3) outermap (F, [a, b], x, matrix ([1, 2], [3, 4])); [ F(a, x, 1) F(a, x, 2) ] [ F(b, x, 1) F(b, x, 2) ] (%o3) [[ ], [ ]] [ F(a, x, 3) F(a, x, 4) ] [ F(b, x, 3) F(b, x, 4) ]
(%i4) outermap (F, [a, b], matrix ([1, 2]), matrix ([x], [y])); [ [ F(a, 1, x) ] [ F(a, 2, x) ] ] (%o4) [[ [ ] [ ] ], [ [ F(a, 1, y) ] [ F(a, 2, y) ] ] [ [ F(b, 1, x) ] [ F(b, 2, x) ] ] [ [ ] [ ] ]] [ [ F(b, 1, y) ] [ F(b, 2, y) ] ]
(%i5) outermap ("+", [a, b, c], [1, 2, 3]); (%o5) [[a + 1, a + 2, a + 3], [b + 1, b + 2, b + 3], [c + 1, c + 2, c + 3]]
A closer examination of the outermap
return value. The first, second,
and third arguments are a matrix, a list, and a matrix, respectively.
The return value is a matrix.
Each element of that matrix is a list,
and each element of each list is a matrix.
(%i1) arg_1 : matrix ([a, b], [c, d]); [ a b ] (%o1) [ ] [ c d ]
(%i2) arg_2 : [11, 22]; (%o2) [11, 22]
(%i3) arg_3 : matrix ([xx, yy]); (%o3) [ xx yy ]
(%i4) xx_0 : outermap (lambda ([x, y, z], x / y + z), arg_1, arg_2, arg_3); [ [ a a ] [ a a ] ] [ [[ xx + -- yy + -- ], [ xx + -- yy + -- ]] ] [ [ 11 11 ] [ 22 22 ] ] (%o4) Col 1 = [ ] [ [ c c ] [ c c ] ] [ [[ xx + -- yy + -- ], [ xx + -- yy + -- ]] ] [ [ 11 11 ] [ 22 22 ] ] [ [ b b ] [ b b ] ] [ [[ xx + -- yy + -- ], [ xx + -- yy + -- ]] ] [ [ 11 11 ] [ 22 22 ] ] Col 2 = [ ] [ [ d d ] [ d d ] ] [ [[ xx + -- yy + -- ], [ xx + -- yy + -- ]] ] [ [ 11 11 ] [ 22 22 ] ]
(%i5) xx_1 : xx_0 [1][1]; [ a a ] [ a a ] (%o5) [[ xx + -- yy + -- ], [ xx + -- yy + -- ]] [ 11 11 ] [ 22 22 ]
(%i6) xx_2 : xx_0 [1][1] [1]; [ a a ] (%o6) [ xx + -- yy + -- ] [ 11 11 ]
(%i7) xx_3 : xx_0 [1][1] [1] [1][1]; a (%o7) xx + -- 11
(%i8) [op (arg_1), op (arg_2), op (arg_3)]; (%o8) [matrix, [, matrix]
(%i9) [op (xx_0), op (xx_1), op (xx_2)]; (%o9) [matrix, [, matrix]
outermap
preserves the structure of the arguments in the return value,
while cartesian_product
does not.
(%i1) outermap (F, [a, b, c], [1, 2, 3]); (%o1) [[F(a, 1), F(a, 2), F(a, 3)], [F(b, 1), F(b, 2), F(b, 3)], [F(c, 1), F(c, 2), F(c, 3)]]
(%i2) setify (flatten (%)); (%o2) {F(a, 1), F(a, 2), F(a, 3), F(b, 1), F(b, 2), F(b, 3), F(c, 1), F(c, 2), F(c, 3)}
(%i3) map (lambda ([L], apply (F, L)), cartesian_product ({a, b, c}, {1, 2, 3})); (%o3) {F(a, 1), F(a, 2), F(a, 3), F(b, 1), F(b, 2), F(b, 3), F(c, 1), F(c, 2), F(c, 3)}
(%i4) is (equal (%, %th (2))); (%o4) true
Next: Debugging, Previous: Function Definition [Contents][Index]