Funções, Expressões e Excepções Trajectórias Óptimas DI/FCT/UNL 1º Semestre 2004/2005 1 Funções e Passagem de Parâmetros • Em muitos casos, ao invocar-se uma função pretende-se obter não apenas um mas vários resultados. • Este requisito é tratado diferentemente em diferentes linguagens de programação, dependendo em grande parte da forma como são passados os parâmetros de uma função. • Em OCTAVE, os parâmetros são passados exclusivamente por valor (com uma excepção - nomes de funções). • Isto quer dizer, que ao especificar-se uma variável no parâmetro de uma função a invocar, o que se passa para a função é o valor da variável. • Durante a função, a variável não pode ser alterada no programa que invoca a função! 2 Passagem de Parâmetros por Valor • Exemplo: Consideremos a função function y = f(x) x = 2*x y = x endfunction; • Se chamada com o valor x = 5, durante a computação da função, esse valor é duplicado e retornado. • Por exemplo, se invocarmos a função na sequência (pode ser ao terminal) ..., x = 5; z = f(x); x, z, ... os valores de x e y reportados no terminal são x = 5 e z = 10. 3 Passagem de Parâmetros por Valor • Exemplo: ..., x = 5; z = f(x); x, z, ... function y = f(x) x = 2*x; y = x; endfunction; A computação da função f pode ser assim explicada: 1. Na instrução x=2*x o que é multiplicado por 2 é o valor passado como parâmetro (i.e. 5) obtendo-se assim o valor 10. 2. Esse valor é atribuído a uma nova variável, também chamada x, mas que é local à função, não sendo alterado o x do programa principal. 3. Na instrução y=2*x, é a nova variável local que é considerada, e portanto, y = 10, valor esse retornado. 4. No “programa principal” esse valor é atribuído à variável z. 4 Passagem de Parâmetros por Referência • Outras linguagens (Pascal, C, C++, ...) permitem a passagem de parâmetros por referência. Neste caso é passada a referência à variável do programa principal, que pode assim ser alterada pela função. • Por exemplo se o parâmetro x fosse passado por referência (indicado com uma notação fictícia) ..., x = 5; z = f(x); x, z, ... function y = f(ref w) w = 2*w; y = w; endfunction; os valores de x e y reportados no terminal seriam x = 10 (a variável w, no interior da função é a mesma variável que a variável x, passada por referência!) e y = 10. 5 Funções com Múltiplos Resultados • A passagem de parâmetros por referência permite que uma função (ou procedimento) passe vários valores para o programa que a invocou. Basta passar por referência as variáveis onde esses valores devem ser “colocados”. • O OCTAVE, não suporta passagem de parâmetros por referência. A computação de vários resultados numa função é conseguida pela computação de um vector de resultados. • Por exemplo, se se pretender que a função f, com argumento x, retorne dois valores, f1 e f2, especifica-se a função como function [f1,f2] = f(x) ... f1 = ...; f2 = ...; ... endfunction; 6 Trajectórias Óptimas • Exemplo: Trajectórias • A anterior função alcance(v,alfa,ka) determinava o alcance de um projéctil lançado com uma velocidade v e ângulo alfa, num meio em que o coeficiente de atrito é ka. • Com essa função pode-se determinar (através de um ciclo) qual o maior alcance, mas não outras características da trajectória em que possamos estar interessados (por exemplo, altura atingida, tempo em que a atinge, tempo total da trajectória ou uma combinação destas características). • Essas características, especificamente a altura atingida e o tempo total da trajectória podem ser retornados por uma função, tiro(v,alfa,ka), especificada de uma forma muito semelhante (a encarnado as diferenças). 7 Trajectórias Óptimas function [xmax, ymax, tmax] = tiro(vi, alfa, ka) dt = 0.01; g = 9.8; t = 0; ymax = 0; x = 0; vx = vi*cos(alfa*pi/180) ; y = 0; vy = vi*sin(alfa*pi/180); ax = - ka * vx ; ay = -g - ka * vy; while y >= 0 t = t + dt; x = x + vx * dt; y = y + vy * dt; if y > ymax ymax = y; endif; vx = vx + ax * dt; vy = vy + ay * dt; ax = - ka * vx; ay = -g - ka * vy; endwhile; xmax = x; tmax = t; endfunction; 8 Programas e Funções • Podemos agora responder a um conjunto de perguntas, assumindo-se uma dada velocidade inicial e coeficiente de atrito, tais como: – Qual a trajectória que atinge maior altura, com alcance mínimo de x_min? Qual a altura e qual o ângulo de disparo? – Qual o alcance máximo atingido com uma trajectória que não dura mais de um tempo limite, t_limite? Qual o seu ângulo de disparo? – Qual é a trajectória “máxima”, isto é cujo produto da altura máxima pelo alcance e pelo tempo é máximo? Qual o seu ângulo de disparo? • Vejamos como responder à última destas questões. 9 Trajectórias Óptimas function [xmax, ymax, tmax, amax] = tiro_maximo(vi, ka) maximo = 0; for alfa = 0:90 [x, y, t] = tiro(vi,alfa,ka); v = x*y*t if v > maximo maximo = v; xmax = x; ymax = y; tmax = t; amax = alfa; endif; endfor; endfunction; 10 Programas e Funções • Em muitas situações não se pretende o melhor mas apenas algo razoável. Consideremos por exemplo a questão: – Será que existe uma trajectória (isto é, um lançamento feito a uma velocidade vi e um ângulo alfa, num meio com coeficiente de atrito ka) em que a altura seja maior que y1 e o alcance maior que x1? • Esquemáticamente, devemos fazer uma pesquisa para todos os valores de vi, alfa e ka (com uma determinada precisão), o que se pode fazer encadeando 3 ciclos para (for). • Assumamos que existem valores mínimos e máximos a respeitar para vi, alfa e ka e que a precisão a utilizar é, respectivamente, de 1ms-1, 1º e 0.1 s-1. 11 Trajectórias Óptimas function [v,a,k]= bom_tiro(xmin,ymin,vmin,vmax,amin,amax,kmin,kmax) for vi = vmin:vmax for alfa = amin:amax for ka = kmin:0.1:kmax [x, y, t] = tiro(vi,alfa,ka); if x > xmin & y > ymin v = vi; a = alfa; k = ka; endif; endfor; endfor; endfor; endfunction; 12 Expressões Booleanas • Este enunciado, embora correcto, é algo ineficiente, já que após a descoberta de uma boa trajectória, todas as restantes trajectórias são ainda calculadas! • Esta ineficiência pode ser corrigida se se alterarem os ciclos “para” por ciclos “enquanto”, mas impondo na condição de entrada que não tenha sido ainda descoberta uma solução. • Essa condição pode ser especificada através de uma expressão booleana, resultante da conjunção de duas expressões booleanas mais simples: – A limitação normal do número de ciclos (p.ex. ka < kmax). – Uma variável booleana, encontrada, que é falsa enquanto não tiver sido encontrada uma solução 13 Expressões Booleanas ... encontrada = 0 vi = vmin; while vi <= vmax & !encontrada alfa = amin; while alfa <= amax & !encontrada ka = kmin; while ka <= kmax & !encontrada [x, y, t] = tiro(vi,alfa,ka); if x > xmin & y > ymin v = vi; a = alfa; k = ka; encontrada = 1 endif; ka = ka + 0.1; endwhile; alfa = alfa + 1; endwhile; vi = vi + 1; endwhile; ... 14 Expressões Booleanas • Expressões booleanas podem ser construidas recursivamente a partir de outras mais simples com os operadores booleanos de – Conjunção, “e” ou “and”, expressa como & em OCTAVE – Disjunção, “ou” ou “or”, expressa como | em OCTAVE – Negação, “não” ou “not”, expressa como ! em OCTAVE • As variáveis booleanas podem tomar os valores verdade ou falso. Em OCTAVE, que só considera variáveis “numéricas”, 0 corresponde a falso e qualquer outro valor a verdade! • De notar que uma variável booleana pode ser atribuído o valor booleano de uma comparação numérica. Por exemplo: encontrada = (x > xmin & y > ymax) 15 Excepções (em ciclos) • Embora as expressões booleanas sejam muito convenientes e permitam escrever programas muito compactos, neste caso a passagem de ciclos “para” para ciclos “enquanto” torna os programas menos “naturais”. • Uma forma mais natural de expressar a intenção de descobrir a solução é indicar que se pretende percorrer todo o intervalo de possibilidades, excepto se se encontrar uma solução! • A especificação de excepções é possível em muitas linguagens de programação. No Octave utiliza-se a instrução “break”, para se terminar excepcionalmente um ciclo. 16 Excepções (em ciclos) • Por exemplo, em vez da forma pode utilizar-se a forma mais simples ... encontrada = 0; vi = vmin; while vi <= vmax & !encontrada ... if condição encontrada = 1; endif; ... vi = vi + 1; endwhile; ... ... for vi = vmin:vmax ... if condição break; endif; ... endfor; ... 17 Excepções (em ciclos) • Infelizmente, a explicitação de excepções não conduz sempre a uma grande simplificação da especificação global do programa. • No caso corrente, como existem três ciclos, ao sair do ciclo interno (de ka) é necessário verificar se se deve igualmente sair do ciclo intermédio (em alfa) e igualmente do ciclo externo (em vi). • Em geral, é conveniente saber se o ciclo terminou de uma forma “normal” ou “excepcional”, pelo que é conveniente continuar a utilizar uma variável booleana para o efeito. • Por exemplo, os três ciclos anteriores (em vi, alfa e ka) podem ser especificados com excepções na forma 18 Excepções (em ciclos) ... encontrada = 0 for vi = vmin: vmax for alfa = amin:amax for ka = kmin:kmax [x, y, t] = tiro(vi,alfa,ka); if x > xmin & y > ymi v = vi; a = alfa; k = ka; encontrada = 1; break; endif; endfor; if encontrada break endif; endfor; if encontrada break endif; endfor; ... 19 Excepções (em funções) • Se, como é o caso corrente, existirem vários ciclos no interior de uma função, e se se detectar no interior do ciclo mais interno que o objectivo da função já foi atingido, existe um mecanismo de excepção que permite terminar a função imediatamente. • Assim, podem imediatamente terminar-se todos os ciclos, sem se ter de verificar para cada um se o ciclo imediatamente interno terminou com uma excepção ou não. • No entanto, poderá ter interesse informar o programa (ou função) que chama a função que pode terminar excepcionalmente, se a sua terminação foi normal ou não, o que pode ser feito através de uma variável adicional que é retornada no valor da função. 20 Trajectórias Óptimas function [v,a,k,descoberta,x,y]= bom_tiro(xmin,ymin,vmin,vmax,amin,amax,kmin,kmax) descoberta = 0; for vi = vmin:vmax for alfa = amin:amax for ka = kmin:0.1:kmax [x, y, t] = tiro(vi,alfa,ka); if x > xmin & y > ymin v = vi; a = alfa; k = ka; descoberta = 1; return; endif; endfor; endfor; endfor; endfunction; 21 Um Pequeno Problema • Em muitas situações, nomeadamente no corpo de ciclos, pretende-se trocar o valor de duas variáveis AB • Por hipótese, façamos A = 2 e B = 5. • O problema é que ao fazer-se uma atribuição, por exemplo AB perde-se o valor inicial de A (fica A=B=5) e a atribuição B A, mantém a situação. • A forma mais simples de resolver este problema é utilizar uma variável secundária onde se guarda o valor inicial de A. As instruções seguintes resolvem pois o problema: CA % C=2 AB % A=5 BC % B=2 22