Introducedilhaão a Fluxo de Programa
====================================

Maxima fornece um do para ciclos iterativos, também contrucedilhaões mais primitivas tais como go.

Definicedilhaões para Fluxo de Programa
=======================================

 -- Funcedilhaão: backtrace ()
 -- Funcedilhaão: backtrace (N)
     Imprime a pilha de chamadas, que é, a lista de funcedilhaões que foram chamadas pela funcedilhaão correntemente activa.

     backtrace() imprime toda a pilha de chamadas.

     backtrace (N) imprime as N mais recentes chamadas a funcedilhaões, incluindo a funcedilhaão correntemente activa.

     backtrace pode ser chamada por um script, uma funcedilhaão, ou a partir da linha de comando interativa (não somente em um contexto de depuracedilhaão).

     Exemplos:

       * backtrace() imprime toda a pilha de chamadas.

               (%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) imprime as N mais recentes chamadas a funcedilhaões, incluindo a funcedilhaão correntemente activa.

               (%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

 -- Operador especial: do
     A declaracedilhaão do é usada para executar iteracedilhaão.  Devido à sua grande generalidade a declaracedilhaão do será  descrita em duas partes. Primeiro a forma usual será dada que é análoga à forma que é usada em muitas outras linguagens de programacedilhaão (Fortran, Algol, PL/I, etc.); em segundo lugar os outros recursos serão mencionados.

     Existem três variantes do operador especial do que diferem somente por suas condicedilhaões de encerramento.  São elas:

       * for VARIÁVEL: VALOR_INICIAL step INCREMENTO thru LIMITE do CORPO

       * for VARIÁVEL: VALOR_INICIAL step INCREMENTO while CONDITION do CORPO

       * for VARIÁVEL: VALOR_INICIAL step INCREMENTO unless CONDITION do CORPO

     (Alternativamente, o step pode ser dado após a condicedilhaão de encerramento ou limite.)

     VALOR_INICIAL, INCREMENTO, LIMITE, e CORPO podem ser quaisquer expressões.  Se o incremento for 1 então "step 1" pode ser omitido.

     A execucedilhaão da declaracedilhaão do processa-se primeiro atribuindo o valor_inicial para a variável (daqui em diante chamada a variável de controle).  Então: (1) Se a variável de controle excede o limite de uma especificacedilhaão thru, ou se a condicedilhaão de unless for true, ou se a condicedilhaão de while for false então o do será encerrado.  (2) O corpo é avaliado.  (3) O incremento é adicionado à variável de controle.  O processo de (1) a (3) é executado repetidamente até que a condicedilhaão de encerramento seja satisfeita.  Pode-se também dar muitas condicedilhaões de encerramento e nesse caso o do termina quando qualquer delas for satisfeita.

     Em geral o teste thru é satisfeito quando a variável de controle for maior que o limite se o incremento for não negativo, ou quando a variável de controle for menor que o limite se o incremento for negativo. O incremento e o limite podem ser expressões não numéricas enquanto essa desigualdade puder ser determinada.  Todavia, a menos que o incremento seja sintaticamente negativo (e.g. for um número negativo) na hora em que a declaracedilhaão do for iniciada, Maxima assume que o incremento e o limite serão positivos quando o do for executado.  Se o limite e o incremento não forem positivos, então o do pode não terminar propriamente.

     Note que o limite, incremento, e condicedilhaão de encerramento são avaliados cada vez que ocorre um ciclo.  Dessa forma se qualquer desses for responsável por muitos cálculos, e retornar um resultado que não muda durante todas as execucedilhaões do corpo, então é mais eficiente escolher uma variável para seu valor anterior para o do e usar essa variável na forma do.

     O valor normalmente retornado por uma declaracedilhaão do é o átomo done. Todavia, a funcedilhaão return pode ser usada dentro do corpo para sair da delcaracedilhaão do prematuramente e dar a isso qualquer valor desejado. Note todavia que um return dentro de um do que ocorre em um block encerrará somente o do e não o block.  Note também que a funcedilhaão go não pode ser usada para sair de dentro de um do dentro de um block que o envolve.

     A variável de controle é sempre local para o do e dessa forma qualquer variável pode ser usada sem afectar o valor de uma variável com o mesmo nome fora da declaracedilhaão do.  A variável de controle é liberada após o encerramento da declaracedilhaão do.

          (%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 que a condicedilhaão while i <= 10 é equivalente a unless i > 10 e também 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

     que fornece 8 termos da série de Taylor para 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

     Esse exemplo calcula a raíz quadrada negativa de 10 usando a iteracedilhaão de Newton- Raphson um maximum de 10 vezes.  Caso o critério de convergêcia não tenha sido encontrado o valor retornado pode ser done. Em lugar de sempre adicionar uma quantidade à variável de controle pode-se algumas vezes desejar alterar isso de alguma outra forma para cada iteracedilhaão. Nesse caso pode-se usar next EXPRESSÃO em lugar de step INCREMENTO. Isso fará com que a variável de controle seja escolhida para o resultado da expressão de avaliacedilhaão cada vez que o ciclo de repeticedilhaão for executado.

          (%i6) for count: 2 next 3*count thru 20 do display (count)$
                                      count = 2

                                      count = 6

                                     count = 18

     Como uma alternativa para for VARIÁVEL: VALOR ...do... a sintaxe for VARIÁVEL from VALOR ...do...  pode ser usada.  Isso permite o from VALOR ser colocado após o step ou proximo valor ou após a condicedilhaão de encerramento.  Se from VALOR for omitido então 1 é usado como o valor inicial.

     Algumas vezes se pode estar interessado em executar uma iteracedilhaão onde a variável de controle nunca seja usada.  Isso é permissível para dar somente as condicedilhaões de encerramento omitindo a inicializacedilhaão e a informacedilhaão de actualizacedilhaão como no exemplo seguinte para para calcular a raíz quadrada de 5 usando uma fraca suposicedilhaão inicial.

          (%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

     Se isso for desejado pode-se sempre omitir as condicedilhaões de encerramento inteiramente e apenas dar o corpo do CORPO que continuará a ser avaliado indefinidamente.  Nesse caso a funcedilhaão return será usada para encerrar a execucedilhaão da declaracedilhaão 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 que return, quando executado, faz com que o valor corrente de x seja retornado como o valor da declaracedilhaão do.  O block é encerrado e esse valor da declaracedilhaão do é retornado como o valor do block porque o do é a última declaracedilhaão do block.)

     Uma outra forma de do é disponível no Maxima.  A sintaxe é:

          for VARIÁVEL in LIST END_TESTS do CORPO

     Os elementos de LIST são quaisquer expressões que irão sucessivamente ser atribuídas para a variável a cada iteracedilhaão do corpo.  O teste opcional END_TESTS pode ser usado para encerrar a execucedilhaão da declaracedilhaão do; de outra forma o do terminará quando a lista for exaurida ou quando um return for executado no corpo.  (De facto, a lista pode ser qualquer expressão não atômica, e partes sucessivas são usadas.)

          (%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

 -- Funcedilhaão: errcatch (EXPR_1, ..., EXPR_N)
     Avalia EXPR_1, ..., EXPR_N uma por uma e retorna [EXPR_N] (uma lista) se nenhum erro ocorrer.  Se um erro ocorrer na avaliacedilhaão de qualquer argumento, errcatch evita que o erro se propague e retorna a lista vazia [] sem avaliar quaisquer mais argumentos.

     errcatch é útil em ficheiros batch onde se suspeita que um erro possa estar ocorrendo o errcatch terminará o batch se o erro não for detectado.

 -- Funcedilhaão: error (EXPR_1, ..., EXPR_N)
 -- Variável de sistema: error
     Avalia e imprime EXPR_1, ..., EXPR_N, e então causa um retorno de erro para o nível mais alto do Maxima ou para o mais próximo contendo errcatch.

     A variável error é escolhida para uma lista descrevendo o erro. O primeiro elemento de error é uma sequência de caracteres de formato, que junta todas as sequências de caracteres entre os argumentos EXPR_1, ..., EXPR_N, e os elementos restantes são os valores de quaisquer argumentos que não são sequências de caracteres.

     errormsg() formata e imprime error. Isso efectivamente reimprime a mais recente mensagem de erro.

 -- Funcedilhaão: errormsg ()
     Reimprime a mais recente mensagem de erro. A variável error recebe a mensagem, e errormsg formata e imprime essa mensagem.

 -- Operador especial: for
     Usado em iteracedilhaões.  Veja do para uma descricedilhaão das facilidades de iteracedilhaão do Maxima.

 -- Funcedilhaão: go (TAG)
     é usada dentro de um block para transferir o controle para a declaracedilhaão do bloco que for identificada com o argumento para go.  Para identificar uma declaracedilhaão, coloque antes dessa declaracedilhaão um argumento atômico como outra declaracedilhaão no block.  Por exemplo:

          block ([x], x:1, loop, x+1, ..., go(loop), ...)

     O argumento para go deve ser o nome de um identificardor aparecendo no mesmo block.  Não se pode usar go para transferir para um identificador em um outro block que não seja o próprio contendo o go.

 -- Operador especial: if
     Representa avaliacedilhaão condicional. Várias formas de expressões if são reconhecidas. if COND_1 then EXPR_1 else EXPR_0 avalia para EXPR_1 se COND_1 avaliar para true, de outra forma a expressão avalia para expr_0.

     if COND_1 then EXPR_1 elseif COND_2 then EXPR_2 elseif ... else EXPR_0 avalia para EXPR_K se COND_K for true e todas as condicedilhaões precedentes forem false. Se nenhuma das condicedilhaões forem true, a expressão avalia para expr_0.

     O comportamento else false é assumido se else for omitido. Isso é, if COND_1 then EXPR_1 é equivalente a if COND_1 then EXPR_1 else false, e if COND_1 then EXPR_1 elseif ... elseif COND_N then EXPR_N é equivalente a if COND_1 then EXPR_1 elseif ... elseif COND_N then EXPR_N else false.

     As alternativas EXPR_0, ..., EXPR_N podem ser quaisquer expressões do Maxima, incluíndo expressões if aninhadas ( if dentro de if). As alternativas não são nem simplificadas nem avaliadas a menos que a correspondente condicedilhaão seja true.

     As condicedilhaões COND_1, ..., COND_N são expressões tais que is(COND_K) avaliem para true ou para false; de outra forma é um erro. Entre outros elementos, condicedilhaões podem compreender operadores lógicos e relacionais como segue.

          Operacedilhaão             Símbolo      Tipo

          menor que            <           infixo relacional
          menor que            <=
            ou igual a                     infixo relacional
          igualdade            =
            (sintática)                    infixo relacional
          negacedilhaão de =         #           infixo relacional
          igualdade (valor)    equal       funcedilhaão relacional
          negacedilhaão de           notequal
            igualdade                      funcedilhaão relacional
          maior que            >=
            ou igual a                     infixo relacional
          maior que            >           infixo relacional
          e                    and         infixo lógico
          ou                   or          infixo lógico
          não                  not         prefixo lógico

 -- Funcedilhaão: map (F, EXPR_1, ..., EXPR_N)
     Retorna uma expressão cujo operador principal é o mesmo que o das expressões EXPR_1, ..., EXPR_N mas cujas subpartes são os resultados da aplicacedilhaão de F nas correspondentes subpartes das expressões.  F é ainda o nome de uma funcedilhaão de n argumentos ou é uma forma lambda de n argumentos.

     maperror - se false fará com que todas as funcedilhaões mapeadas (1) parem quando elas terminarem retornando a menor expi se não forem todas as expi do mesmo comprimento e (2) aplique fn a [exp1, exp2,...] se expi não forem todas do mesmo tipo de objecto.  Se maperror for true então uma mensagem de erro será dada nas duas instâncias acima.

     Um dos usos dessa funcedilhaão é para mapear (map) uma funcedilhaão (e.g. partfrac) sobre cada termo de uma expressão muito larga onde isso comumente não poderia ser possível usar a funcedilhaão sobre a expressão inteira devido a uma exaustão de espacedilhao da lista de armazenamento no decorrer da computacedilhaão.

          (%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]

 -- Funcedilhaão: mapatom (EXPR)
     Retorna true se e somente se EXPR for tratada pelas rotinas de mapeamento como um átomo.  "Mapatoms" são átomos, números (incluíndo números racioanais), e variáveis subscritas.

 -- Variável de opcedilhaão: maperror
     Valor por omissão: true

     Quando maperror é false, faz com que todas as funcedilhaões mapeadas, por exemplo

          map (f, expr_1, expr_2, ...))

     (1) parem quando elas terminarem retornando a menor expi se não forem todas as expi do mesmo comprimento e (2) aplique f a [expr_1, expr_2, ...] se expr_i não forem todas do mesmo tipo de objecto.

     Se maperror for true então uma ,mensagem de erro é mostrada nas duas instâncias acima.

 -- Funcedilhaão: maplist (F, EXPR_1, ..., EXPR_N)
     Retorna uma lista de aplicacedilhaões de F em todas as partes das expressões EXPR_1, ..., EXPR_N. F é o nome de uma funcedilhaão, ou uma expressão lambda.

     maplist difere de map (F, EXPR_1, ..., EXPR_N) que retorna uma expressão com o mesmo operador principal que EXPR_I tem (excepto para simplificacedilhaões  e o caso onde map faz um apply).

 -- Variável de opcedilhaão: prederror
     Valor por omissão: true

     Quando prederror for true, uma mensagem de erro é mostrada sempre que o predicado de uma declaracedilhaão if ou uma funcedilhaão is falha em avaliar ou para true ou para  false.

     Se false, unknown é retornado no lugar nesse caso.  O modo prederror: false não é suportado no código traduzido; todavia, maybe é suportado no código traduzido.

     Veja também is e maybe.

 -- Funcedilhaão: return (valor)
     Pode ser usada para sair explicitamente de um bloco, levando seu argumento.  Veja block para mais informacedilhaão.

 -- Funcedilhaão: scanmap (F, EXPR)
 -- Funcedilhaão: scanmap (F, EXPR, bottomup)
     Recursivamente aplica F a EXPR, de cima para baixo.  Isso é muito útil quando uma factoracedilhaão completa é desejada, por exemplo:

          (%i1) exp:(a^2+2*a+1)*y + x^2$
          (%i2) scanmap(factor,exp);
                                              2      2
          (%o2)                         (a + 1)  y + x

     Note o caminho através do qual scanmap aplica a dada funcedilhaão factor para as subexpressões constituintes de EXPR; se outra forma de EXPR é apresentada para scanmap então o resultado pode ser diferente.  Dessa forma, %o2 não é recuperada quando scanmap é aplicada para a forma expandida de exp:

          (%i3) scanmap(factor,expand(exp));
                                     2                  2
          (%o3)                      a  y + 2 a y + y + x

     Aqui está um outro exemplo do caminho no qual scanmap aplica recursivamente uma funcedilhaão dada para todas as subexpressões, incluindo expoentes:

          (%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) aplica F a EXPR de baixo para cima.  E.g., para f indefinida,

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

     Neste caso, obtém-se a mesma resposta pelos dois métodos.

 -- Funcedilhaão: throw (EXPR)
     Avalia EXPR e descarta o valor retornado para o mais recente catch.  throw é usada com catch como um mecanismo de retorno não local.

 -- Operador especial: while
     Veja do.

 -- Funcedilhaão: outermap (F, A_1, ..., A_N)
     Aplica a funcedilhaão F para cada um dos elementos do produto externo A_1 vezes A_2 ... vezes A_N.

     F é o nome de uma funcedilhaão de n argumentos ou uma expressão lambda de n argumentos. Cada argumento A_K pode ser uma lista simples ou lista aninhada ( lista contendo listas como elementos ), ou uma matrz, ou qualquer outro tip de expressão.

     O valor de retorno de outermap é uma estrutura aninhada. Tomemos X como sendo o valor de retorno. Então X tem a mesma estrutura da primeira lista, lista aninhada, ou argumento matriz, X[i_1]...[i_m] tem a mesma estrutura que a segunda lista, lista aninhada, ou argumento matriz, X[i_1]...[i_m][j_1]...[j_n] tem a mesma estrutura que a terceira lista, lista aninhada, ou argumento matriz, e assim por diante, onde M, N, ... são os números dos índices requeridos para acessar os elementos de cada argumento (um para uma lista, dois para uma matriz, um ou mais para uma lista aninhada). Argumentos que não forem listas ou matrizes não afectam a estrutura do valor de retorno.

     Note que o efeito de outermap é diferente daquele de aplicar F a cada um dos elementos do produto externo retornado por cartesian_product. outermap preserva a estrutura dos argumentos no valor de retorno, enquanto cartesian_product não reserva essa mesma estrutura.

     outermap avalia seus argumentos.

     Veja também map, maplist, e apply.

     Exemplos: Exemplos elementares de outermap. Para mostrar a a combinacedilhaão de argumentos mais claramente, F está indefinida à esquerda.

          (%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]]

     Uma explanacedilhaão final do valor de retorno de outermap. Os argumentos primeiro, segundo, e terceiro são matriz, lista, e matriz, respectivamente. O valor de retorno é uma matriz. Cada elementos daquela matriz é uma lista, e cada elemento de cada lista é uma matriz.

          (%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