Índice
Seguinte

Programação no Maxima


Programação no Maxima tem dois diferentes aspectos:

O uso da liguagem de programação do Maxima é fortemente recomendado para trabalhos sérios. Programar  em Lisp é trabalho para usuários avançados. Ocasionalmente, pode ser necessário usar ambas as linguagens de programaçã para resolver um problema. O uso de arquivos é um candidato a programação nas duas linguagens.


Um programa simples do Maxima nada mais é que uma seqüência de comando do Maxima. Para estar apto a reproduzir uma computação longa mais tarde, é muitas vezes prudente gravar esses comandos em um arquivo de lote. Tal arquivo de protocolo é de fato um programa do Maxima.

No capítulo sobre  Tubos abraçando Curvas Espaciais usamos essa definição para calcular a tangente de uma curva espacial:

tangent(fn, x) :=
diff(fn(x), x) / ((diff(fn(x), x) . diff(fn (x), x))^(1/2))$

Isso é uma definição direta, mas tem uma falha séria: calcula a derivada de f três vezes. Pode ser muito melhor calcular a derivada somente uma vez e guardá-la para usaromais tarde.

tangent(fn, x) :=
block ( [df: diff(fn(x), x)],
df / (df . df)^(1/2)
)$

Explanações:


A Estrutura de Expressões

Uma expressão é ou um átomo ou uma estrutura que é construída com um operador e uma lista de argumentos. A função op é usada para acessar o operador de uma expressão, a função args é usada para obter uma lista de todos os argumentos. Alguns exemplos mostram isso:

 op (a + b + c);
+ args(a + b + c); [c, b, a] op (sin(3*x)); sin args (sin(3*x)); [3 x] op (f(a, b, g(c), d)); f args (f(a, b, g(c), d)); [a, b, g(c), d]

Note que exp(x) é traduzida em um expoente para a constante %e:

 op(exp(2*x));
^ args(exp(2*x)); [%e, 2 x]

Uma Função que coleta os Operadores de uma Expressão

A expressão não atômica contém sempre exatamente um operador e uma coleção de argumentos que estão em uma expressão anterior. uma expressão é dessa forma uma árvore. Isso é algumas vezes útil para percorrer aquela árvore  e coletar todos os operadores. O conhecimento de todos os operadores usados pode ser usado para selecionar simplificações promissoras e omitir simplificações que obviamente são desnecessárias no contexto de alguns operadores.

Essa seção faz uma explanação como tal função é programada.

Resumo da Solução desejada

Queremos uma função que receba uma expressão e retorne uma lista com todos os operadores da expressão:

allOps(a);
[] allOps(sin(x) + cos(x)); [cos, sin, +] allOps(sin(x) + 2*sin(2*x)); [sin, *, +]

A lista respondida pode conter todo operador encontrado pelo menos uma vez. Repetições não são sesejadas. A última expressão exemplifica esse requerimento.

A função allOps é muito simples: delega o exame da expressão a uma função que recebe uma subexpressão e uma lista de operadores que forem encontrados então:

allOps(expression):=
block( [ ],
allOpsPriv (expression, [])
);

A função allOpsPriv requer mais trabalho. Uma primeira tentativa de manusear somente casos simples:

  /* incomplete preliminary version  */
allOpsPriv(expression, opList) :=
 block ( [x],
        if atom(expression)
           then opList
           else
             (x: op(expression),
              cons(x, opList)
             ) 
        );

Essa definição maneja dois casos simples:

A definição usa uma declaração if para identificar e manusear ambos os casos. Uma declaração if sempre responde o valor de sua alternativa avaliada como seu resultado. Para uma expressão atômica, a declaração if acima  responde o valor de opList. o ramo else da declaração if contém duas declarações que são escritas entre parêntesis. O resultado de cons(x, opList) é o resultado completo da declaração if quando o ramo else é selecionado para avaliação.

Note que a definição de allOpsPriv usa uma variável temporária para armazenar o operador de uma expressão não atômica. Isso não é realmente necessário, isso pode ser escrito:

cons(op(expression), opList)

Quando tentamos a definição que temos agora, obtemos:

allOps(a);
[] allOps(sin(x) + cos(x)); [+]

O exame de uma subexpressão é obviamente omitida. Para uma solução completa temos que:

Um refinamento dessa definição requer ações adicionais no ramo else da declaração if.

O código adicionado é mostrado em negrito:

allOpsPriv(expression, opList) :=
block ( [x, args, newList],
if atom(expression)
then opList
else
(x: op(expression),
args: args(expression),
newList:
cons(x, opList),
for arg in args do
newList: allOpsPriv(arg, newList),
newList
) );

Aqui usamos o elemento da linguagem

for arg in args do
<statement>

o qual iterage sobre todos os elementos arg na lista args. Note que a variável arg não é declarada como uma  variável local de bloco: A declaração daquela variável local de bloco está implícita e sua abrangência é a declaração que é avaliada abaixo dela por meio do iterador.

Um rápido teste revela ambas as consideráveis melhorias e uma imperfeição do algorítmo:

allOps(a);
[] allOps(sin(x) + cos(x)); [cos, sin, +] allOps(sin(x) + 2*sin(2*x)); [sin, *, sin, *, +]

Os operadores sin e * são emncionados duas vezes. Isso não é realmente uma surpresa, uma vez que nós não verificamos se um operador já se encontra na lista antes de adicioná-lo. Isso é todavia facilmente corrigido aqui:

allOpsPriv(expression, opList) :=
block ( [x, args, newList],
if atom(expression)
then opList
else
(x: op(expression),
args: args(expression),
newList: if member (x, opList)
then opList
else
cons(x, opList),
for arg in args do
newList: allOpsPriv(arg, newList),
newList
)
);

Você encontra esse código no arquivo  allOps.mc. Quando você copiar aquele arquivo no subdiretório user de sua instalação do Maxima, você pode chamá-lo com

batch("allOps");

A função allOps coleta todos os operaadores de uma expressão. Para fazer isso, ela tem que percorrer completamente uma expressão. Para um problema similar o exame completo da expressão não é sempre necessário:

Quando estamos olhando pela presença de um dado operador em uma expressão, podemos parar o eexame da expressão tão logo encontremos o operador que estamos procurando.

containsOp(expression, opList) :=
block ( [x, args, found],
if atom(expression)
then false else (x: op(expression), args: args(expression), if member (x, opList) then true else (found : false, for arg in args while not found do
found: containsOp(arg, opList),
found)
)
);

Aqui usamos uma variante do elemento de linguagem usado anteriormente for arg in args do:

for arg in args while not found do
<statement>

que iterage sobre os elementos arg na lista args enquanto a condição se mantiver. A iteração termina quando a condição tornar-se nil ou quando todos os elementos da lista forem processados processados.

Índice
Seguinte