Página Web do Sadao Massago

Página de Maxima

\( \DeclareMathOperator{\abs}{abs} \)

Definindo função no Maxima

Autor: Sadao Massago
instituição: DM-UFSCar
web: http://www.dm.ufscar.br/~sadao
data: 2017-12-25
site do maxima e manual
http://maxima.sourceforge.net/
http://maxima.sourceforge.net/documentation.html
Um dos manuais mais recomendado é
http://maxima.sourceforge.net/docs/manual/en/maxima.html
A versão em PDF em
http://maxima.sourceforge.net/docs/manual/en/maxima.pdf
A versão em português (um pouco desatualizado) em
http://maxima.sourceforge.net/docs/manual/pt/maxima.html

Para executar <cntrl><ENTER> executa o bloco de comando onde o cursor está <ctrl>R executa todo arquivo
Para acrescentar códigos e comentários F5 abre o campo de comando F6 abre o campo de comentario (texto)
Estes e outros comandos estão no menu "cell".
F1 abre a ajuda do Maxima. Se o cursor estiver sobre a palavra, procurara por esta paravra na ajuda.

Assume que ja leu o "part01_intro.wxm" ("Introdução ao maxima")

(%i1) /* limpando a memoria */
kill(all);
reset();
\[\mathrm{\tt (\%o0) }\quad \mathit{done}\]\[\mathrm{\tt (\%o1) }\quad [\%,\_,\mathit{\_\_},\mathit{lispdisp},\mathit{tr-unique},\mathit{labels}]\]

1 Criando funções complexas

Inicialmente, definimos uma função simples O simbolo := é usado para criar funções. O parâmetro da função é substituido na expressão a direita e retornado.
Para imprimir o código da função, usa-se o dispfun.
Para retornar a definição de função, usa-se o fundef()

(%i5) f(x) := x^2+1$
f;
dispfun(f)$

fundef(f)$
\[\mathrm{\tt (\%o3) }\quad f\]\[\mathrm{\tt (\%t4) }\quad \mathrm{f}\left( x\right) :={{x}^{2}}+1\]

Inicialmente, vamos criar funções que executam em várias etapas. Diferentemente da função com expressão simples, queremos efetuar vários cálculos.
A seguir, a versão simples de baskara definido pela única expressão. Como queremos retornar dois valores, retornaremos como listas.

(%i7) bhaskara1(a,b,c) := [(-b-sqrt(b^2-4*a*c))/(2*a), (-b+sqrt(b^2-4*a*c))/(2*a)]$

bhaskara1(2,3,1);
\[\mathrm{\tt (\%o7) }\quad [-1,-\frac{1}{2}]\]

Quando retornado uma lista de valores, poderá receber com lista de variáveis, uma para cada valor.

(%i10) [x1, x2] : bhaskara1(2,3,4);
x1;
x2;
\[\mathrm{\tt (\%o8) }\quad [\frac{-\sqrt{23}\cdot i-3}{4},\frac{\sqrt{23}\cdot i-3}{4}]\]\[\mathrm{\tt (\%o9) }\quad \frac{-\sqrt{23}\cdot i-3}{4}\]\[\mathrm{\tt (\%o10) }\quad \frac{\sqrt{23}\cdot i-3}{4}\]

Na bhaskara1(), foi usado o valor de discriminante duas vezes, o que poderia ter economizado se tiver armazenado em uma variável.

(%i12) bhaskara2(a,b,c) := (
 d : b^2-4*a,c,
 [(-b-sqrt(d))/(2*a), (-b+sqrt(d))/(2*a)]
)$
bhaskara2(1,2,3);
\[\mathrm{\tt (\%o12) }\quad [-1,-1]\]

bhaskara2 é uma função com sequência de comandos. ":=" foi usado para atribuir expressão a função bhaskara2(a,b,c). A sequência de comandos pode ser colocado entre parenteses, delimitado pela virgula (não pelo ponto e virgula ou dolar). Neste caso, o resultado da última expressão será retornada.

(%i15) d : 1;
bhaskara2(2,2,3);
d;
\[\mathrm{\tt (\%o13) }\quad 1\]\[\mathrm{\tt (\%o14) }\quad [\frac{-2\cdot i-2}{4},\frac{2\cdot i-2}{4}]\]\[\mathrm{\tt (\%o15) }\quad -4\]

Note que, no exemplo acima, o valor de "d" armazenado antes da execussão de bhaskara2() foi substituido pelo valor obtido dentro da bhaskara2().

Para evitar que isto aconteça, deverá usar o "block" e d na lista de variáveis locais que é colocado no começo do block.
O block significa bloco de comandos e o maxima executará a sequencia de comandos contidos nele. O primeiro parametro do bloco é lista de variáveis locais, sendo que pode ser lista vazia, caso não haja variáveis locais.
A sequencia de comandos devem ser separados pela vírgula e nao pelo ponto e virgula ou dolar, o que requer cuidados. O valor retornado pela função é do último comando.
Dentro do block, poderá conter controle de fluxo também, o que veremos mais adiante.
Assim, sempre que definir função com mais de um comando, é recomendável que use block, mesmo que não tenha variáveis locais (evite usar delimitar somente pelos parenteses sem o block, como na baskara2).

(%i19) bhaskara3(a,b,c) := block([d],
 d : b^2-4*a,c,
 [(-b-sqrt(d))/(2*a), (-b+sqrt(d))/(2*a)]
)$

d : 1;
bhaskara3(2,2,3);
d;
\[\mathrm{\tt (\%o17) }\quad 1\]\[\mathrm{\tt (\%o18) }\quad [\frac{-2\cdot i-2}{4},\frac{2\cdot i-2}{4}]\]\[\mathrm{\tt (\%o19) }\quad 1\]

A função definido anteriormente não checa se "a" é nulo. Se a = 0, não seria equação de segundo grau.

(%i22) bhaskara4(a,b,c) := block([d],
 if a = 0 then (
   print("ERRO: bhaskara4(): a = 0"),
   return (0)
 ),    
 d : b^2-4*a,c,
 [(-b-sqrt(d))/(2*a), (-b+sqrt(d))/(2*a)]
)$

bhaskara4(2,3,1);
bhaskara4(0, 1, 2);
\[\mathrm{\tt (\%o21) }\quad [-1,-\frac{1}{2}]\mbox{}\\ERRO:\,bhaskara4():\,a\,=\,0\]\[\mathrm{\tt (\%o22) }\quad 0\]

Agora vamos pegar o valor de a,b e c da expressão.

(%i25) bhaskara5(eq, x) := block([a, b, c, d, y],
 y : expand(lhs(eq)-rhs(eq)),
 if hipow(y, x) # 2 then (
   print("ERRO: bhaskara5(): Não é equação de segundo grau"),
   return (0)
 ),
 a : coeff(y, x, 2),
 b : coeff(y, x, 1),
 c : coeff(y, x, 0),
      
 d : b^2-4*a,c,
 [(-b-sqrt(d))/(2*a), (-b+sqrt(d))/(2*a)]
)$

bhaskara5(2*x^2+3*x+5=0,x);
bhaskara5(2*y^2+3*y=5*(y-1),y);
\[\mathrm{\tt (\%o24) }\quad [-1,-\frac{1}{2}]\]\[\mathrm{\tt (\%o25) }\quad [\frac{2-2\cdot i}{4},\frac{2\cdot i+2}{4}]\]

hipow(expr, var) retorna o grau do polinômio expr na variável var. coeff(expr, var, i) retorna o coeficiente de var^i. Caso i for omitido, será assumido como 1. Para que hipow e coeff funcionem direito, é necessário que a expressão seja expandida.
# é o simbolo para ser diferente.

Agora, vamos definir a função fatorial
Note que, ao colocar variável local na lista, poderá atribuir o valor ao mesmo tempo.

(%i27) fatorial1(n) := block([p : 1],
 for i:2 thru n do
   p : p*i,
 p
)$

fatorial1(5);
\[\mathrm{\tt (\%o27) }\quad 120\]

Note que i nao foi colocado na lista de variáveis locais. No caso do laco de contagem, indice do laço existirá somente dentro do laco por padrão. Assim, não interfere no valor de fora do laço.

Agora a versão do fatorial que usa o parâmetro como variável local

(%i29) fatorial2(n) := block([p:n],
 while n>1 do (
   n : n-1,
   p : p * n
 ),
 p
)$

fatorial2(5);
\[\mathrm{\tt (\%o29) }\quad 120\]

2 Retornando do laço

O laço é interrompido pelo comando return (valor). No entanto, return sai do último laço (e não da função) Se não estiver dentro do laço, retornará do block() e se ele for a funcao, retornara da funcao.
Observe a seguinte uso incorreto do return

(%i31) bad_positive_list_p(l) := block([],
 for i:1 thru length(l) do
   if l[i] <= 0 then return (false),
 true
)$

bad_positive_list_p([1, 2, 3, -1]);
\[\mathrm{\tt (\%o31) }\quad \mbox{true}\]

A intenção era retornar false quando encontra valores negativos ou nulos, mas retorna true sempre. Isto porque return faz parar o laço, mas não sai da função. (Só sai da função se não estiver dentro do laço). Assim, retorna o último valor que é true
Para contornar, podera criar uma variavel auxiliar flag e atribuir valores antes de retornar do laco.

(%i33) positive_list_p(l) := block([flag : true],
 for i:1 thru length(l) do
   if l[i] <= 0 then (
     flag : false,
     return
   ),
 flag
)$

positive_list_p([1, 2, 3, -1]);
\[\mathrm{\tt (\%o33) }\quad \mbox{false}\]

Se quer sair de város laços de uma vez, deverá usar o throw/catch

(%i36) positive_matrix_entry_p(M) := block([],
 catch(
   for i:1 thru length(M) do
     for j:1 thru length(M[1]) do
       if M[i,j] <= 0 then
         throw (false),
   true
 )
)$

positive_matrix_entry_p(matrix([1,2],[1,3]));
positive_matrix_entry_p(matrix([1,2],[-1,3]));
\[\mathrm{\tt (\%o35) }\quad \mbox{true}\]\[\mathrm{\tt (\%o36) }\quad \mbox{false}\]

throw gera uma excessão e faz sair de todos laços/funções até encontrar catch.

No exemplo a seguir, o if condicao then return foi usado para simular o laco do tipo do/until (tipo do/while, mas para quando a condição for verdadeira)

(%i38) fatorial3(n) := block([p:n],
 if n <= 1 then return (1),
 do (
   n : n-1,
   p : p * n,
   if(n<=1) then return (p)
 )
)$

fatorial3(20);
\[\mathrm{\tt (\%o38) }\quad 2432902008176640000\]

3 Função anônima

Função anonima é uma função sem nome que pode ser usado como função. Ele pode ser atribuida na variável e usar variável como função, ou criar função sem nome para ser passado diretamente na rotina que requer função como parámetro.
Função anônima no maxima é criado pelo comando lambda
lambda ([parametros], expressão)

(%i41) f : lambda([x], x^2+1);

f(3);
map(f, [1,2,3]);
\[\mathrm{\tt (\%o39) }\quad \mathrm{lambda}\left( [x],{{x}^{2}}+1\right) \]\[\mathrm{\tt (\%o40) }\quad 10\]\[\mathrm{\tt (\%o41) }\quad [2,5,10]\]

Função anomima pode ser passado diretamente para parâmetro da função que requer função.

(%i42) map(lambda([x], x^2+1), [1,2,3]);
\[\mathrm{\tt (\%o42) }\quad [2,5,10]\]

Ou calcular o valor dele diretamente

(%i43) lambda ([x], x^2) (a);
\[\mathrm{\tt (\%o43) }\quad {{a}^{2}}\]

Para usar a variável local, use o block no corpo da função.

(%i46) s : 1;
map(lambda ([n], block([s], s:0, thru n do s : s+n, s)), [1,2,3]);
s;
\[\mathrm{\tt (\%o44) }\quad 1\]\[\mathrm{\tt (\%o45) }\quad [1,4,9]\]\[\mathrm{\tt (\%o46) }\quad 1\]

4 Função recursiva

Apesar da função recursiva costuma ser menos eficiente que a iterativa, existem casos que a recursividade simplifica muito a implementação. Para criar uma função recursiva, basta chamar a propria função que está definindo, mas cuidado para ter critério de parada da chamada para evitar recursividade inifnita.
Então vamos ver um exemplo de fatorial recursiva.

(%i48) fatorial5(n) := if n <= 1 then 1 else n*fatorial5(n-1)$

fatorial5(10);
\[\mathrm{\tt (\%o48) }\quad 3628800\]

5 Parámetros opcionais

Para usar parâmetros opcionais, coloca o [lista] no final de parâmetros. Todos parâmetros opcionais serão colocados na lista. Se lista é vazia, nao houve parâmetros opcionais

(%i51) teste_param(a,b,[l]) := block([],
     print ("a = ", a, ", b = ", b),
     if emptyp(l) then
       print("Nao parâmetros opcionais")
     else (
       print("Parâmetros opcionais: "),
       for x in l do
         print(x),
       print("fim dos parâmetros opcionais")
     )
   )$

teste_param(2,3);
teste_param(2,3,4,5, 6, 7);
\[\mbox{}\\a\,=\,2,\,b\,=\,3\mbox{}\\Nao\,há\,parâmetros\,opcionais\]\[\mathrm{\tt (\%o50) }\quad Nao\,há\,parâmetros\,opcionais\mbox{}\\a\,=\,2,\,b\,=\,3\mbox{}\\Parâmetros\,opcionais:\,4567\mbox{}\\fim\,dos\,parâmetros\,opcionais\]\[\mathrm{\tt (\%o51) }\quad fim\,dos\,parâmetros\,opcionais\]

Em geral, checamos se tem ou não o parâmetro opcional e na ausência dele, atribuimos um valor padrão.

(%i56) /* obrigatório: f; opcional: x0, N, e */
newton(f, [l]) := block([h, x0, N, e, y, df, x],
 if emptyp(l) then x0 : 0 else x0 : l[1],
 if length(l) <= 1 then N : 10 else N : l[2],
 if length(l) <= 2 then e : 0.5E-10 else e : l[3],  
 /* define(df(x), diff(f(x), x)), */ /* este, usa f global */
 define(df(x), diff(apply(f, [x]), x)),  /* este, usa f do parâmetro */
 /*
 print(apply(f, [x])),
 dispfun(f),
 dispfun(df),    */
 thru N do (
   /* valor numérico aproximado em ponto flutuante */
   /* print(ev(df(x0), float)), */
   y : apply(f, [x0]),
   /* print("x0 = ", x0, "y= ", y),            */
   h : ev(-y/df(x0), float),
   x : ev(x0 + h, float),
   /* print("h = ", h, "x= ", x),  */
   if abs(h) <= e then return,
   x0 : x
 ),
 x
)$

newton(lambda([x], x^2+x+1));
newton(lambda([x], x^2+x+1), 0.5);
newton(lambda([x], x^2+x+1), 0.5, 5);
newton(lambda([x], x^2+x+1), 0.5, 5, 0.5E-5);
\[\mathrm{\tt (\%o53) }\quad 0\]\[\mathrm{\tt (\%o54) }\quad -0.9008226384810346\]\[\mathrm{\tt (\%o55) }\quad 0.26380961783359\]\[\mathrm{\tt (\%o56) }\quad 0.26380961783359\]

Por exemplo, é possível criar funções que recebem qualquer número de parámetros

(%i59) soma([l]) := apply("+", l);

soma(1,2,3,4,5);
soma(a, b, c);
\[\mathrm{\tt (\%o57) }\quad \mathrm{soma}\left( [l]\right) :=\mathrm{apply}\left( +,l\right) \]\[\mathrm{\tt (\%o58) }\quad 15\]\[\mathrm{\tt (\%o59) }\quad c+b+a\]

Parámetros opcionais também pode ser usado para função anônima

(%i60) (lambda([[l]], apply("+", l)))   (1,2,3,4,5);
\[\mathrm{\tt (\%o60) }\quad 15\]

6 Pacotes e arquivos de lotes

Quando cria conjunto de funções úteis, costumamos criar um arquivo separado para ele e carregar quando necessário. Este arquivo é chamado de pacotes e é carregado pelo load(). Para tanto, basta criar um arquivo maxima válido com definiçẽs de funções e carregar pelo comando load() como qualquer outro pacote.
No caso de estar editando no wxmaxima, lembre-se que o formato wxmx nao é reconhecido diretamente pelo maxima. Assim, exporte como mac (formato nativo da maxima) ou salve como wxm (que pode ser editado pelo wxmaxima)
Por exemplo, suponha que tenha criado o arquivo meu_pacote.wxm com seguinte contéudo:
meu_const : sqrt(2); minha_soma([l]) := appy("+", l);
Então exporte para o formato mac, usando Arquivo→exportar e na extensão, escolher .mac ("maxima batch file (*.mac)" ).
O pacote é carregado pelo load("meu_pacote.mac"); carrega este arquivo e meu_const e minha_soma() ficará disponível para uso.
Note que a extensão .mac no load() é necessário por pacote estar na pasta pessoal e não na pasta do maxima. Para pacotes padrão de maxima, a extensão .mac nao é usado no nome de arquivo, por estar na pasta do maxima e também por poder carregar a versão lisp (já compilado).

(%i63) load("part04_meu_pacote.mac");
meu_const_sqrt2;
minha_soma(1,2,3,4,5);
\[\mathrm{\tt (\%o61) }\quad part04\_meu\_pacote.mac\]\[\mathrm{\tt (\%o62) }\quad \sqrt{2}\]\[\mathrm{\tt (\%o63) }\quad 15\]

O comando load() carrega o pacote, mas não mostra o resultado de execussão. ele é usado para disponibilizar funções e variáveis e executar configurações que é o objetivo de um pacote.

Se quer que o arquivo seja executado de fato, mostrando todas saídas como se estivesse digitado no arquivo corrente, deverá usar o batch() Para o batch(), poderá passar tanto o arquivo mac como o wxm, mas não poderá passar o wxmx.

(%i64) batch("part04_meu_pacote.wxm");
\[\mbox{}\\\mbox{read and interpret file: \#p/home/sadao/MEGA/core/maxima/mactutor/part04\_meu\_pacote.wxm}\mbox{}\\\mbox{(\%i65) meu\_const\_sqrt2:sqrt(2)}\]\[\mathrm{\tt (\%o65) }\quad \sqrt{2}\mbox{}\\\mbox{(\%i66) minha\_soma([l]):=apply("+",l)}\]\[\mathrm{\tt (\%o66) }\quad \mathrm{minha\_soma}\left( [l]\right) :=\mathrm{apply}\left( +,l\right) \mbox{}\\\mbox{(\%i67) "Created with wxMaxima"}\]\[\mathrm{\tt (\%o67) }\quad part04\_meu\_pacote.wxm\]
Created with wxMaxima.