Página de Maxima
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(); |
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)$ |
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); |
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; |
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); |
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; |
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; |
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); |
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); |
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); |
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); |
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]); |
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]); |
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])); |
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); |
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]); |
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]); |
Ou calcular o valor dele diretamente
| (%i43) | lambda ([x], x^2) (a); |
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; |
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); |
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 há 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); |
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); |
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); |
Parámetros opcionais também pode ser usado para função anônima
| (%i60) | (lambda([[l]], apply("+", l))) (1,2,3,4,5); |
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); |
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"); |