Apêndice 2 - Linguagem de programação Mathematica (breve referência)
Neste apêndice abordamos o sistema computacional Mathematica, mencionando, basicamente, somente
aqueles aspectos que são essenciais para entender os programas, escritos na linguagem de programação
Mathematica, que são apresentados ao longo deste texto.
Embora o sistema Mathematica suporte vários paradigmas da programação, iremos aqui limitar-nos a
apresentar apenas as construções essenciais para a programação recursiva e imperativa, não fazendo
qualquer referência explícita nem à programação funcional, nem à reescrita (paradigma particularmente
apropriado para a chamada computação simbólica). Para uma introdução (mais completa) ao sistema
Mathematica, e à programação neste nos vários paradigmas referidos, pode consultar-se1 p.ex. [8].
Expressões.
As expressões matemáticas, numéricas e simbólicas, podem ser escritas no sistema Mathematica seguindo
uma sintaxe muito próxima da sintaxe usual em matemática.
No que respeita às operações aritméticas, os símbolos dos operadores são os usuais: + (adição), (subtracção), * (multiplicação) e / (divisão)2.
Os operadores de comparação são também denotados pelos símbolos usuais (<, >, <= ou ≤, >= ou ≥),
com excepção de que se usa == em vez de3 =, para o teste de igualdade (numérica4), e se usa != em vez de
≠, para a desigualdade5.
Já no que respeita aos conectivos Booleanos (conectivos lógicos), embora se possa ir a uma tabela de
símbolos e usar os símbolos usuais da lógica e da matemática, como ¬, ∧ e ∨, os símbolos padrão no
Mathematica não são esses, usando-se
!p (ou Not[p]) para a “negação de p”
p&&q (ou And[p,q]) para a “conjunção de p e q”
p||q (ou Or[p,q]) para a “disjunção de p e q”
1 Especificamente sobre o sistema Mathematica pode consultar-se: “S. Wolfram, The Mathematica book, Wolfram Media, 3ª
edição, 1996”.
2 Podemos ainda recorrer a uma tabela de símbolos para escrever quocientes, potências, raízes quadradas (bem como integrais,
somatórios, etc.) tal como é ususal em matemática, e se ilustra a seguir: 2 , x 2 ,
3
x2 , 3 4,
20
20
i=2
1
∑ i 2, ∫
1
dx, etc.
x
3 Que tem outro significado na linguagem de programação Mathematica, sendo aí usado como sinal de atribuição (como
ilustraremos à frente).
€
4 Existe ainda um operador para testar a igualdade/identidade simbólica (operador denotado por ===, sendo a sua negação
denotada por =!=), mas não abordaremos aqui as diferenças entre os dois testes de igualdade (limitando-nos neste texto a
considerar a igualdade numérica).
5 Em relação à desigualdade, podemos ir à tabela de símbolos e escrever também ≠.
493
Refira-se a propósito que a avaliação no Mathematica de uma conjunção p&&q é sequencial: se a avaliação
da expressão booleana p retornar False, já não é avaliada a expressão booleana q (sendo retornado
False). E a avaliação de uma disjunção também se processa analogamente, de uma forma sequencial.
Funções predefinidas.
O Mathematica dispõe de imensas funções predefinidas. O seu nome começa sempre por uma Maíuscula e
é, para as funções conhecidas, o que se espera, escrito em língua inglesa.
Por exemplo, as funções cosseno e seno são descritas, respectivamente, pelos nomes Cos e Sin. Se
invocarmos a função cosseno aplicada ao ponto π/4:
Cos[
π
]
4
obtemos
€
1
2
uma vez que o Mathematica, sempre que possível, trabalha com valores exactos.
€
Se quisermos obter um valor aproximado devemos recorrer à função N, que por defeito dá um valor
aproximado do resultado com 6 algarismos significativos. Se quisermos obter uma maior precisão,
devemos escrever, como segundo argumento da função N, o número de dígitos de precisão que
pretendemos. Assim, invocando
N[Cos[
π
]] e
4
N[Cos[
π
],10]
4
obtém-se, respectivamente:
€
0.707107 e 0.7071067812
€
Note-se que podemos obter informação sobre o que faz uma função predefinida, avaliando
? nome da função
Se avaliarmos
?N
obtém-se
N[expr] gives the numerical value of expr. N[expr, n]
attempts to give a result with n-digit precision
Parênteses rectos e curvos e chavetas.
Saliente-se que, como os exemplos anteriores ilustraram, a aplicação de uma função faz-se (no sistema
Mathematica) colocando os argumentos (separados por vírgulas, no caso de ser mais do que um) entre
parênteses rectos, e não entre parênteses curvos (como é usual em matemática)!
Os parênteses curvos são utilizados apenas para agrupar expressões (podendo ser, desse modo, usados
para ultrapassar as usuais precedências dos operadores), enquanto que as chavetas são usadas para a
construção de listas (como veremos).
494
A função predefinida Timing e a constante Null.
Uma função que utilizamos no texto (no capítulo 12) é a função Timing. Uma invocação
Timing[exp]
retorna (uma lista da forma)
{tempo,valor}
onde o tempo refere o tempo gasto em segundos na6 avaliação da expressão7 exp, e o valor refere o
valor obtido na avaliação da expressão exp.
Se escrevermos
Timing[exp;]
é omitido do output o valor da expressão exp, sendo retornado (uma lista da forma)
{tempo,Null}
onde Null é uma constante que denota uma expressão (atómica) especial sem qual valor. Se mandarmos
avaliar Null nada é escrito no ecrã.
Tipos.
Em Mathematica existem os seguintes tipos básicos:
• Tipos numéricos:
• Integer (os inteiros, expressos na forma usual; p.ex. 3, -4, etc.)
• Rational (racionais, expressos como um quociente de inteiros; p.ex. 1/3, etc.)
• Real (reais, expressos recorrendo ao ponto decimal e não à vírgula; p.ex. 2., 3.4, etc.)
• Complex (complexos, expressos na forma usual, com I designando a
−1 ; p.ex. 2+3I, etc.)
• Tipo Symbol (o tipo dos nomes):
Os elementos deste tipo são nomes formados por uma letra eventualmente
seguida de uma ou mais
€
letras ou números.
Alguns destes nomes representam constantes e funções predefinidas no Mathematica. Os restantes
nomes podem ser usados para variáveis. Como os nomes predefinidos começam sempre por uma
maíuscula, é conveniente que os nomes escolhidos pelo utilizador para variáveis (ou para nomes de
funções) comecem por uma minúscula, evitando assim conflitos com os identificadores predefinidos (de
qualquer forma, quando tais conflitos ocorrem, o Mathematica avisa-nos).
Refira-se ainda, a propósito, que o Mathematica é uma linguagem de programação “atipada”: as
variáveis utilizadas não são declaradas como sendo de um certo tipo, podendo denotar valores de
diferentes tipos. O Mathematica possui regras que permitem avaliar as várias operações, sendo uma
expressão avaliada por aplicação dessas regras; quando não existe qualquer regra disponível que seja
aplicável à avaliação (reescrita) de uma dada expressão, ou quando a sua avaliação envolve a aplicação
6 Não incluindo, nesse tempo, o tempo gasto a formatar e escrever o output.
7 Nos casos considerados no capítulo 12, a expressão em causa (a que é aplicado o Timing) corresponde à invocação de uma
função/programa.
495
de uma operação a valores “inadequados” (como uma divisão por zero), pára a avaliação da expressão
em causa, e esta é retornada na forma que estava no momento em avaliação (no último caso,
normalmente acompanhada de uma mensagem de erro).
• Tipo String (o tipo das mensagens):
Os elementos deste tipo são (quaisquer) sequências de caracteres entre aspas, e podem ser encarados
como mensagens. Eventuais operações que se encontrem numa mensagem não são avaliadas.
Avaliando p.ex. 2+3 e “2+3” obtém-se, respectivamente:
5
e
2+3
Toda a informação que não seja de um destes tipos tem de ser representada no Mathematica recorrendo
às listas, que analisaremos à frente.
Atribuição (imediata).
Há duas formas de atribuição que podem ser usadas no Mathematica: a atribuição imediata e a diferida.
Aqui falaremos apenas da primeira.
Uma regra de atribuição (imediata) toma a forma
nome = exp
e a execução de uma atribuição deste tipo consiste em avaliar a expressão exp (do lado direito do sinal =) e
em guardar o valor8 assim obtido na variável denotada pelo nome que se encontra no lado esquerdo do
sinal =.
Estas regras, que se se podem ler como se segue “atribuir (o valor de) exp à variável nome“, são
normalmente usadas para “memorizar” valores em variáveis.
Por exemplo, uma atribuição da forma
a = 3
guarda em (na variável) a o valor 3. A partir desse momento, em qualquer expressão em que a ocorra, a
denota o valor 3.
O valor guardado na variável a pode ser alterado em virtude de uma nova atribuição a essa variável. Se
efectuarmos a atribuição
a = a+1
então a passará a denotar o valor 4.
Podemos ainda apagar o valor memorizado em a efectuando9
Clear[a]
A expressão exp do lado direito de uma atribuição
nome = exp
8 Valor que é escrito no ecrã, salvo se a atribuição nome=exp for seguida de um ;, em cujo caso nada é escrito no ecrã (ver o
que se diz a seguir sobre o sequenciamento).
9 Após a avaliação da expressão Clear[a], a variável a passa a denotar-se a si própria (avaliando a, obtém-se a). Por outro
lado, podemos mesmo remover a variável a da nossa sessão de trabalho, efectuando Remove[a].
496
pode ser a definição de uma função (ver à frente), em cujo caso a atribuição em causa é vista como estando
a dar aquele nome à função.
Sequenciamento.
O sequenciamento de acções é efectuado recorrendo ao ponto e vírgula (como é usual nas linguagens de
programação). Se avaliarmos, por exemplo
a = 5; b = a+2; a+b
obtém-se (é escrito no ecrã, como output)
12
Uma sequência da forma (com um ponto e vírgula no fim)
a = 7; b = a+1; a+b;
é interpretada como
a = 7; b = a+1; a+b; Null
e corresponde a efectuar, em sequência: atribuir 7 a a; atribuir 8 (o resultado da avaliação de a+1) a b;
avaliar a expressão a+b; e, finalmente, avaliar a expressão Null.
Como o Mathematica apenas retorna como output, escrito no ecrã, o valor da última expressão
avaliada, e como Null denota uma expressão especial sem qualquer valor, se avaliarmos
a = 7; b = a+1; a+b;
nada é escrito no ecrã.
No entanto as acções referidas foram efectuadas. Se mandarmos avaliar a variável
b
obtemos (o valor que nela está guardado de momento)
8
Podemos também questionar o Mathematica sobre o que está associado a um determinado nome
introduzido pelo utilizador, escrevendo
?b
obtendo-se neste caso
Global`b
b = 8
o que mostra claramente que a atribuição acima (b=a+1) foi efectuada. A mensagem Global`b significa
informalmente que b é um dos nomes introduzidos pelo utilizador10 na sessão de trabalho em curso.
Escrita de resultados.
Como já referimos, o Mathematica apenas escreve no ecrã o valor da última expressão avaliada. Assim, se
queremos escrever no ecrã um resultado intermédio de uma computação (ou escrever uma mensagem no
meio de uma computação), devemos recorrer ao comando (específico) de impressão Print. A execução de
Print[exp1 ,...,expn ]
10 E não removido, por recurso a um Remove (ver nota de rodapé anterior).
497
tem como efeito a escrita sucessiva, na mesma linha, do valor das expressões exp1 , ..., expn (seguida de
mudança de linha).
Listas.
Uma lista não é mais do que uma sequência de elementos (admitindo repetições). A importância das listas
no Mathematica decorre do facto, já referido, de toda a informação que não seja de um tipo básico ter de ser
representada no Mathematica recorrendo a listas. Por essa razão, o Mathematica disponibiliza um tipo de
listas extremamento rico, com imensas funções predefinidas11 que permitem manipular as listas de uma
forma simples e extremamente eficiente.
Uma lista pode ser definida directamente em extensão, enumerando explicitamente os elementos que a
compõem, pondo estes, separados por vírgulas, entre chavetas, ou como argumentos da função predefinida
(de construção de listas) List.
Por exemplo, a lista vazia é representada por
{}
ou por
List[]
e a expressão
{5, D[Sin[x],x], {3,4}, a}
ou
List[5, D[Sin[x],x], {3,4}, a]
representa a lista cujo primeiro elemento é o número 5, o segundo elemento é a expressão da derivada do
sen(x) (i.e. a expressão Cos[x]), o terceiro elemento é a lista formada pelo 3 e pelo 4 , e o último
elemento é a letra a (que poderá estar a denotar-se a si própria, ou poderá estar a denotar um outro qualquer
valor, que nela tenha sido guardado em virtude de uma atribuição anterior).
Como o exemplo anterior ilustra, numa lista podemos guardar elementos de diferentes tipos. Listas
formadas só por listas, todas do mesmo comprimento, podem ser encaradas como matrizes, e a elas serem
aplicadas operações específicas de matrizes (como soma, produto, determinante, etc.).
Uma lista pode também ser definida por compreensão, através da referência à regra de cálculo que
permite gerar os seus elementos. Para esse efeito o Mathematica disponibiliza três funções predefinidas:
Table, Array e Range. Questionando o Mathematica sobre estas funções, obtém-se (onde se suprime
parte da informação disponibilizada, por não ser para aqui essencial):
? Range
Range[imax] generates the list {1, 2, ... , imax}.
Range[imin, imax] generates the list {imin, ... , imax}.
Range[imin, imax, di] uses step di
? Array
Array[f, n] generates a list of length n, with elements f[i].
Array[f, {n1, n2, ... }] generates an n1 by n2 by ... array of
nested lists, with elements f[i1, i2, ... ].
11 No que se segue referiremlos apenas algumas dessas funções.
498
? Table
Table[expr, {imax}] generates a list of imax copies of expr.
Table[expr, {i, imax}] generates a list of the values of expr
when i runs from 1 to imax. Table[expr, {i, imin, imax}]
starts with i = imin. Table[expr, {i, imin, imax, di}] uses
steps di. Table[expr, {i, imin, imax}, {j, jmin, jmax}, ... ]
gives a nested list. The list associated with i is outermost
Uma lista pode também ir sendo construída, através da manipulação de outras listas, recorrendo
nomeadamente a operações de inserção e de extracção de elementos. Seguem-se algumas das principais
operações disponíveis para esse fim:
• Append[w,x] (onde w denota uma lista)
Constrói uma lista que é igual à lista que se obtém quando se acrescenta (o valor denotado por) x no
fim da lista (denotada por) w.
• Prepend[w,x]
(onde w denota uma lista)
Retorna a lista que se obtém quando se acrescenta x no início da lista em (denotada por) w.
• Insert[w,x,n] (onde w denota uma lista e n um inteiro)
Retorna a lista que se obtém quando se insere x na lista12 em w na posição n (se n for igual a zero,
obtém-se um erro; se n for igual ao número de elementos de w mais um, x é colocado no fim da lista
em w; se n for maior que o número de elementos de w mais um, obtém-se um erro; se n for menor que
zero, as posições são contadas da direita para a esquerda13).
Por exemplo, após a avaliação de
a=Append[{14},7];b=Insert[a,4,3];c=Insert[a,4,2];d=Insert[a,4,-1]
a guardará a lista {14,7}, b guardará a lista {14,7,4}, c a lista {14,4,7} e d a lista
{14,7,4}.
• Join[w1,w2,...,wn] (onde w1,w2,...,wn denotam listas)
Retorna a lista que se obtém quando se junta as listas em w1,w2,...,wn.
• Rest[w]
(onde w denota uma lista)
Retorna a lista que se obtém da lista (denotada por) w , quando se retira desta o primeiro elemento
(obtendo-se um erro se w estiver vazia).
• Delete[w,n] (onde w denota uma lista e n um inteiro)
Retorna a lista que se obtém quando se retira da lista em w o elemento que está na posição n.
12 Note-se que a lista argumento destas operações não é alterada pela sua execução (salvo se o resultado lhe estiver a ser
atribuído através de um comando/instrução/regra de atribuição).
13 Este procedimento é análogo para as outras operações sobre listas que têm um argumento que denota uma posição da lista.
Por essa razão não o referiremos mais. Igualmente não referiremos mais, em geral, as possíveis situações de erro, para não
alongar a exposição.
499
• Take[w,n]
(onde w denota uma lista e n um inteiro)
Retorna a lista que se obtém quando se extrai da lista em w os primeiros n elementos14 (i.e.
Take[w,n] denota a lista formada pelos primeiros n elementos da lista em w).
• Drop[w,n]
(onde w denota uma lista e n um inteiro)
Retorna a lista que se obtém quando se retira da lista em w os primeiros n elementos.
(Para retirar o último elemento de lista em w, podemos escrever Drop[w,-1].)
Dispomos igualmente de funções que nos permitem aceder aos elementos que estão na primeira e na
última posição de uma lista, bem como ao elemento que está numa posição especificada da lista:
• First[w]
(onde w denota uma lista)
Retorna o primeiro elemento da lista em w (obtendo-se erro se w estiver vazia).
• Last[w]
(onde w denota uma lista)
Retorna o último elemento da lista em w.
• w[[n]] (onde w denota uma lista e n um inteiro)
Retorna o elemento que está na posição n da lista em w.
Se w for uma lista de listas, então w[[n]][[k]] pode ser abreviado por w[[n,k]]. Por exemplo,
se w for a lista {{6,5},{7,8,4},{6}}, então w[[2,3]] denotará o terceiro elemento da lista
que está na segunda posição de w, i.e. o valor 4.
Podemos também alterar o elemento que está numa dada posição de uma lista, como se segue
• ReplacePart[w,x,n] (onde w denota uma lista e n um inteiro)
Retorna a lista que se obtém quando se substitui na lista em w o elemento que está na posição n por x.
E podemos alterar a lista que está guardada numa determinada variável, através da atribuição de uma nova
lista à variável, como em
a={7,-25,18}
ou alterando apenas o elemento que está numa dada posição n da lista guardada na variável a, através de
uma atribuição da forma a[[n]]=x (que tem o mesmo efeito que a=ReplacePart[a,x,n]). Por
exemplo, supondo que a guarda a lista {7,-25,18}, então, após a atribuição
a[[2]]=0
a variável a passará a denotar a lista {7,0,18}.
Finalmente, uma referência apenas a outras duas funções disponíveis sobre listas, muito úteis e que
utilizamos em alguns programas Mathematica apresentados no texto:
• Length[w]
(onde w denota uma lista)
Retorna o comprimento (o número de elementos) da lista em w.
Por exemplo, Length[{5,{6,7,-4,8},5,{}}] retornará 4.
14 Os últimos n elementos, se n for negativo.
500
• Select[w,f] (onde w denota uma lista e f um predicado15)
Retorna a lista formada pelos elementos da lista w que satisfazem o predicado16 f.
Por exemplo, Select[{5,6,7,4,8},EvenQ] retornará a lista formada pelos elementos da lista
argumento que satisfazem o predicado EvenQ (no caso um predicado predefinido17, que retorna True
se aplicado a um número par e False no caso contrário), ou seja retornará a lista {6,4,8}).
Funções.
Pode considerar-se que programar em Mathematica consiste em definir funções. Uma função pode ser
definida por abstracção funcional, ou através de atribuições paramétricas diferidas. Neste apêndice apenas
abordaremos a técnica da definição de funções por abstracção funcional.
Para definir funções, por (o que denominaremos de) abstracção funcional, recorre-se em Mathematica à
construção
Function[{p1,...,pn},corpo]
onde {p1,...,pn} é uma lista (eventualmente vazia) de nomes, que representam os parâmetros (formais)
da função, e o corpo é uma expressão ou sequência de expressões Mathematica (simples ou complexas),
que representa a regra de cálculo do resultado da função. Quando a função tem apenas um parâmetro p,
podemos escrever simplesmente
Function[p,corpo]
Por exemplo, a função muito simples a seguir, associa a cada número o seu quadrado
Function[n, n^2 (* ou, indo à tabela de símbolos, n2 *)]
(Note-se que podemos escrever comentários, no meio de código Mathematica, colocando o comentário
entre (* e *) 18.)
Saliente-se que a entidade anterior é uma função, pelo que podemos aplicá-la a um argumento, mesmo
sem lhe dar qualquer nome. Por exemplo, avaliando
Function[n,n^2][5]
obtém-se
25
15 Uma função predefinida, ou definida pelo utilizador, com resultado True ou False.
16 Um elemento x satisfaz um predicado f se f[x] retornar True.
17 A generalidade dos predicados predefinidos terminam em Q (que podemos ver como mnemónico de “Query”). Existem,
contudo, também predicados predefinidos que não terminam em Q, mas não abordaremos aqui as diferenças entre os dois tipos
de predicados predefinidos.
18 Um Notebook Mathematica está organizado em células, delimitadas por um parêntese recto no lado direito da janela, tendo
cada célula um tipo associado, que pode ser texto (text), entrada (input), saída (outpu), gráficos (graphics), entre outros.
Embora, por omissão, quando escrevemos no Mathematica a célula seja de inpurt, podemos definir tal célula como sendo p.ex.
de text, o tipo natural para escrita de texto ou comentários longos. O input (i.e. o código/texto escrito numa célula de input) é
avaliado premindo simultaneamente as teclas shift e return, sendo o resultado retornado numa célula de output. Como se refere
em cima, comentários (normalmemente curtos) podem ainda ser escritos no meio de código de input, colocando-os entre (* e
*): qualquer texto que esteja, numa célula de input, entre (* e *) não é avaliado pelo Mathematica.
501
Mas, naturalmente, podemos dar nomes às funções que definimos, o que facilita a sua invocação
(evitando, nomeadamente, ter de estar a repetir a definição da função em suas sucessivas invocações): tal é
feito através de uma atribuição da função ao nome pretendido.
Por exemplo, chamando à função atrás de quad (abreviatura de quadrado), através da atribuição19
quad = Function[n, n^2];
pode em seguida invocar-se facilmente a função e escrever p.ex.
quad[3]+quad[quad[2]]
expressão cuja avaliação retornará 25.
No caso da função definida ter mais do que um parâmtetro20, como no exemplo a seguir
med = Function[{x,y},(x+y)/2];
então numa sua invocação temos de indicar um argumento por cada um dos parâmetros, separando-os por
vírgulas, como se ilustra a seguir
med[5,1]
É importante perceber o modo como se processa a avaliação da invocação de uma função. Aquando da
invocação de uma função os parâmteros são substituídos no corpo da função (em todo o sítio onde aí
ocorram), pelos correspondentes argumentos, a expressão assim obtida é avaliada e o valor assim obtido é
retornado como resultado dessa invocação.
Por exemplo, a invocação med[5,1] anterior, conduziria à expressão
(5+1)/2
expressão que seria avaliada, sendo o seu valor retornado como output (e escrito no ecrã)
3
Assim, ao contrário do que se passa na maioria das linguagens de programação de carácter imperativo,
os parâmetros não podem ser usados como variáveis locais.
Por exemplo, se introduzirrmos a definição
f = Function[n,n=n+1;n^2];
e em seguida avaliarmos
f[4]
obtemos uma mensagem de erro, e não o valor 25, uma vez que o que o Mathematica faz é substituir o
parâmetro n pelo argumento 4, no corpo da função f, conduzindo à sequência
4=4+1;4^2
que é em seguida avaliada, dando origem a uma situação de erro, em virtude de não podermos atribuir 4+1
a 4 (só podemos efectuar atribuições a variáveis).
19 O ponto e vírgula ; no fim da atribuição serve para evitar que, após a avaliação dessa atribuição, apareça no ecrã a função
definida Function[n,n2] e que foi guardada no nome quad (recorde-se o que se disse atrás acerca de uma atribuição
imediata e do sequenciamento, com um ponto e vírgula no fim).
20 Em cujo caso, recorde-se, os parâmetros têm de ser apresentados na forma de uma lista, colocando-os entre chavetas,
separados por vírgulas.
502
Expressões condicionais.
Muitas vezes pretendemos definir uma função cujo resultado depende de uma certa condição (a que por
vezes se chama em matemática de funções definidas por ramos). Tal é nomeadamente a situação em
qualquer função/programa recursivo, em que o resultado é definido directamente para o chamado caso base
da recursão, sendo para os outros casos definido recursivamente (o chamado passo da recursão) 21.
Para esse efeito recorremos às chamadas expressões condicionais, que no Mathematica tomam a forma
If[condição, expressão1, expressão2]
e que se podem ler como se segue: “se (if) condição, então (then) expressão1, senão (else) expressão2”.
Como a leitura anterior sugere, a avaliação de uma expressão condicional
If[condição, expressão1, expressão2]
corresponde a:
i) avaliar a condição;
ii) e, se esta for verdadeira22, retornar como resultado o valor da expressão1;
iii) caso contrário, retornar como resultado o valor da expressão2.
Recorrendo a esta construção é muito simples definir, por exemplo, um programa recursivo para o
cálculo do factorial de um número natural (programa cuja eficiência é analisada no capítulo 12):
factRec = Function[n,
If[n==0, 1, n*factRec[n-1]]
];
Construções essenciais para a programação imperativa.
Para terminar este apêndice, vejamos apenas (algumas d)as principais contruções que o Mathematica
disponibiliza para a chamada programação imperativa, e que são utilizadas nos programas apresentados no
texto (e, nomeadamente, no capítulo 12).
Como se refere em [8], no paradigma da programação imperativa utiliza-se (normalmente) uma variável
para guardar o resultado desejado, bem como outras variáveis auxilares do processo de cálculo. O valor
memorizado nessas variáveis é sucessivamente alterado (actualizado), até se obter o estado final pretendido,
através do encadeamento de acções elementares (tipicamente atribuições) que são organizadas por
intermédio de três formas básicas de composição de acções: sequencial, alternativa e iterativa.
A composição sequencial permite mandar executar acções (e avaliar expressões) em sequência,
recorrendo ao ;, como já se abordou atrás.
21 O caso base da recursão corresponde à condição inicial, no caso das sucessões definidas por recorrência, e o passo da
recursão à equação de recorrência: veja-se o capítulo 8, onde a problemática da definição recursiva de funções é abordada (e
fundamentada).
22 I.e. se o resultado da avaliação da condição for True.
503
A composição de acções em aternativa, em função de uma dada condição, é efectuada recorrendo23 à
construção If, acabada de referir, mas que no contexto da programação imperativa é vista não tanto como
uma alternativa entre (a avaliação de) duas expressões, mas mais com o ênfase na execução em alternativa
de duas acções (ou sequências de acções, simples ou complexas).
Mais concretamente, e numa linguagem informal, podemos dizer que na programação imperativa se
recorre tipicamente a duas formas de if’s:
• o chamado if-then-else, da forma
If[condição, acção1, acção2]
cuja execução corresponde a:
i) avaliar a condição;
ii) e, se esta for verdadeira, executar a acção1 (que poderá ser uma sequência de acções);
iii) caso contrário, executar a acção2 (que poderá também ser uma sequência de acções).
• e o chamado if-then, da forma
If[condição, acção1]
(ou If[condição, acção1,])
que pode ser visto como uma abreviatura de
If[condição, acção1,Null]
e cuja execução corresponde (portanto) a:
i) avaliar a condição;
ii) e, se esta for verdadeira, executar a acção1 (que poderá ser uma sequência de acções);
iii) caso contrário, “não fazer nada” 24 (avaliar Null, que não produz qualquer resultado).
Finalmente, com a composição iterativa (ou repetitiva) pretende-se mandar executar repetidamente uma
certa acção enquanto uma certa condição é verdadeira (ou um número especificado de vezes). É esta forma
de composição de acções, em iteração, que dá poder à programação imperativa, desempenhando de algum
modo neste paradigma o papel da recursão no paradima da programação recursiva.
As composições iterativas podem ser obtidas recorrendo a três construções do Mathematica: While,
Do e For. Como nos programas apresentados neste texto apenas se utiliza a construção While, só
abordaremos esta construção iterativa neste apêndice.
A construção While toma a forma
While[condição,acção]
e a sua execução corresponde a:
i)
avaliar a condição;
ii)
e, se esta for falsa, “não fazer nada” (terminando a execução do While25);
iii) caso contrário, é executada a acção (que poderá ser uma acção simples, como uma atribuição, ou uma
sequência de acções, um If, ou um outro While), e em seguida volta-se a executar o mesmo While.
23 Existem outras construções de carácter alternativo, mas que não abordaremos aqui.
24 Concluindo a execução do If em causa, e passando à execução da acção que se seguir a esse If no programa em questão
(isto no caso de se lhe seguir alguma outra acção).
25 Passando à execução da instrução/acção que se lhe seguir no programa (se existir alguma).
504
Isto é, na execução de um While[condição,acção] a acção é executada sse a condição for verdadeira,
em cujo caso a acção é executada repetidamente, até que a (avaliação da) condição dê falso26, altura em que
a execução do While pára.
Refira-se, antes de prosseguir, que por vezes usamos o termo ciclo para designar um
While[condição,acção], em cujo caso a acção referida é chamada do passo do ciclo, designando-se ainda
a condição em causa da guarda do ciclo (terminologia que usamos algumas vezes neste texto,
nomeadamente no capítulo 12).
Se mandarmos avaliar um If é retornado como resultado o valor da última expressão avaliada.
Por exemplo, avaliando
x=2; y=3; If[x<0, x*y, x-y]
obtém-se o valor -1, resultante da avaliação da expressão x-y.
E, se avaliarmos
x=2; y=3; If[x>0, y=y+1; x=x+y, y=y-1; x=x-y]
para além das alterações (dos valores das variáveis x e y) decorrentes das atribuições efectuadas, é retornado
como resultado o valor da última expressão avaliada, i.e. o valor 6 (resultante da avaliação da expressão
x+y, avaliação efectuada no âmbito da execução da atribuição x=x+y).
Pelo contrário, se avaliarmos
x=7; y=3; While[x>0, y=y+1; x=x-y]
nada aparece no ecrã: a avaliação de um While não se traduz pela produção de um resultado explícito
(retornando Null).
Deste modo, os efeitos de um While são (sempre) obtidos (colateralmente), por alteração de (do valor
guardado em) variáveis, através de atribuições efectuadas no seu passo. (Qual o valor das variáveis x e y
após a avaliação da sequência de instruções anterior?)
De posse destas construções, torna-se já possível construir qualquer programa imperativo. No entanto,
uma última observação é importante para os codificar no Mathematica.
Considere-se, por exemplo, a sequência de instruções/acções a seguir
r=1; i=1;
While[i<=n, r=r*i; i=i+1]
Facilmente se verifica, que após a execução desta sequência de instruções, está guardado na variável r o
factorial do valor que esteja guardado na variável n (supondo que este é um número natural).
No entanto, como o While não retorna qualquer valor, se mandarmos avaliar o código anterior
nenhum valor é retornado. Assim, o que se faz em geral, no Mathematica, para retornar o resultado
pretendido, é avaliar no final a variável que guarda esse resultado27.
26 Se tal nunca acontecer, então a execução do While não termina.
27No caso em que o resultado desejado não está guardado (numa só) variável, sendo obtido por combinação de valores
guardados em várias variáveis, então é avaliada no final a expressão que traduz tal combinação.
505
De posse desta informação, já é simples construir uma função que, se aplicada a um número natural,
retorna o seu factorial:
factImp = Function[n,
r=1; i=1;
While[i<=n, r=r*i; i=i+1];
r
];
e se mandarmos avaliar p.ex.
factImp[4]
obtém-se
24
A invocação anterior, embore calcule correctamente o factorial do argumento, tem contudo efeitos
colaterais (que, pelo menos no caso anterior, não são desejados28).
De facto, após a invocação anterior, se mandarmos avaliar a variável i, obtém-se 5. O que se passa é
que as sucessivas atribuições a29 i, decorrentes da avaliação de factImp[4], repercutiram-se para além
desta avaliação, uma vez que não especificámos que as variáveis utilizadas deviam ser consideradas como
variáveis locais, a utilizar apenas no cálculo pretendido do factorial de n.
A construção
Module[{nome1,...,nomen}, expressão]
especifica que os nomes30 mencionados devem ser tratados como locais à avaliação da31 expressão referida.
E, definindo a função para o cálculo do factorial (de forma imperativa) como se segue
28 No paradigma da programação imperativa tira-se muitas vezes partido desses efeitos colaterais, em situações em que estes
são devidamednte “contextualizados”, mas sai fora do âmbito deste apêndice abordar esses aspectos metodológicos ligados à
programação imperativa (veja-se p.ex. [8]).
29 Ao definir uma função podemos recorrer não só a funções predefinidas, como a qualquer função previamente introduzida
pelo utilizador na sessão do Mathematica em curso. Podemos ainda utilizar variáveis que não são definidas como variáveis locais
(do modo que se refere a seguir), mas eventuais alterações do valor dessas variáveis, decorrentes de atribuições a elas
efectuadas no decorrer de uma invocação da função, repercutem-se para além dessa invocação.
30 Saliente-se que mesmo que seja um só nome, ele terá de estar entre chavetas. Por outro lado, refira-se que os nomes
especificados podem ser utilizados, no cálculo efectuado (dentro do Module), como variáveis (onde se memorizam valores
durante esse cálculo), ou como nomes de funções (aí definidas), mas em qualquer caso como nomes locais, em que as suas
“definições”, efectuadas no cálculo realizado dentro do Module, se “extinguem” no fim desse cálculo.
31 Onde o termo expressão está aqui a ser usado em sentido amplo, podendo tratar-se quer de uma expressão matemática
simples, quer de uma sequência de instruções/acções do tipo das que temos estado a referir, e que são tipicamente usadas nos
programas imperativos.
506
factImp = Function[n, Module{r,i},
r=1; i=1;
While[i<=n, r=r*i; i=i+1];
r
]];
já não se obtêm os efeitos colaterais referidos.
Se avaliarmos, por exemplo
i=1; factImp[4]
obtém-se como reultado (como output)
24
mas o valor da variável32 i permanece inalterável. Se avaliarmos
i
obtém-se
1
Com esta (muito breve) introdução ao sistema Mathematica, deverá ser possível entender os programas
apresentados no capítulo 12, mesmo que não se tenha tido qualquer contacto prévio com tal sistema
computacional, e mesmo que não se tenha prática de programação33 (cumprindo-se assim o objectivo deste
segundo, e último, apêndice).
32 Variável que é vista como sendo diferente da variável local i, utilizada para o cálculo de factImp[4].
33 Estamos aqui a referir-nos a entender (ler) os programas apresentados. Não se afirma que, só com esta introdução à
linguagem de programação Mathematica, será fácil a qualquer pessoa, sem nenhuma prática de programação, construir (por si)
todos os programas apresentados no capítulo 12. Não nos debruçámos aqui sobre qualquer metodologia para a construção de
programas: não é objectivo deste texto, ensinar a programar.
507
Download

Apêndice2