Introdução à programação e aos algoritmos
4.4 Funções que Retornam Mais de um Resultado
Escreva uma função que receba um valor inteiro e devolva seu quadrado.
EXERCÍCIO 4.9
Escreva uma função fahrenheit(celsius) que receba um valor de
uma temperatura Celsius e devolva seu equivalente em Fahrenheit. Usando esta
função, imprima os valores equivalentes das temperaturas Celsius em Fahrenheit
entre 0 e 100, com incrementos de 10.
EXERCÍCIO 4.10
Escreva uma função celsius(fahrenheit) que receba um valor de
uma temperatura Fahrenheit e devolva seu equivalente em Celsius. Usando esta
função, imprima os valores equivalentes das temperaturas Fahrenheit em Celsius
entre 0 e 300, com incrementos de 10. Coloque comandos para que o usuário
escolha os valores de início, m e passo que serão usados como argumentos da
função.
EXERCÍCIO 4.11
Escreva uma função distancia(x1, y1, x2, y2) que
devolva a distância entre dois pontos cujas coordenadas cartesianas são (x1,
y1) e (x2, y2).
4.4 FUNÇÕES QUE RETORNAM MAIS DE UM
RESULTADO
“Primeiro resolva o problema, então escreva o código”. John Johnson.
Mencionei, logo no início deste capítulo, que uma função retorna apenas um resultado. Isso continua sendo verdade, mas podemos obter mais de uma resposta se essa resposta vier encapsulada.
Para tanto, apresentarei um algoritmo interessante de cálculo do
valor de pi. Existem diversos métodos, este é apenas um dentre tantos, mas o que faz com que esse método seja único é basear-se em probabilidades.
Este exemplo também serve para mostrar que programar é muito
mais que apenas escrever automaticamente alguns comandos em um editor de texto, é muito mais que saber a sintaxe de uma linguagem. Programar é uma atividade intelectual. Para isso, você precisa pensar, estudar, buscar soluções de problemas. Diversas áreas do conhecimento podem ajudá-lo a buscar melhores soluções. Não despreze nada.
Vamos ao problema: você precisa calcular o valor de pi, mas não
tem nenhum instrumento à mão que lhe forneça este valor. Então você se lembra de que pi aparece naturalmente nas fórmulas que envolvem círculos e circunferências. Digamos que você tenha um círculo de raio r. Você sabe que a área de um círculo é dada pela fórmula
Figura 4.5 Círculo com raio 1 e centro em 0,0.
em que Ac é a área do círculo e r o seu raio. Desse modo, você deduz que se souber a área de um círculo, o valor de pi virá por:
Para facilitar ainda mais o seu cálculo, se o raio for igual a 1, a área do círculo será exatamente igual a pi.
Mas, como calcular a área do círculo se você não tem pi? Existe uma figura que ajuda você a calcular a área facilmente: o quadrado. Coloque o círculo dentro do quadrado e veja que a área do círculo
será um pouco menor que a área do quadrado (Figura 4.6).
Se o raio r do círculo for igual a 1, o lado do quadrado será igual a 2 e consequentemente sua área será 4 (2 ). Um círculo de raio 1 tem área igual a pi. Portanto a área do círculo (Ac) está para a área do quadrado (Aq) assim como pi está para 4:
consequentemente:
Figura 4.6 Círculo dentro de um quadrado de lado 2.
Ainda está difícil de resolver? Neste ponto é que entra a relação
Ac/Aq. Vamos usar um método probabilístico. O que aconteceria se eu fizesse um alvo com o desenho do círculo dentro do quadrado e lançasse dardos aleatoriamente em direção a esse alvo? Estou jogando dardos às cegas, e a única certeza que tenho é de que os dardos vão atingir o quadrado. Quanto mais dardos eu lançar, mais a proporção entre os dardos que caíram dentro do círculo e o número total de dardos será próxima da relação entre a área do círculo e a do quadrado; parece mágica, mas é probabilidade.
Esse método tem um nome: método de Monte Carlo. Monte Carlo é uma cidade da Europa famosa por seus cassinos. Nosso jogo aqui é achar o valor de pi.
É claro que não vamos jogar dardos verdadeiros. Programar é criar um modelo do mundo. Faremos um programa que simule esse lançamento de dardos. Vamos gerar coordenadas cartesianas de pontos distribuídos aleatoriamente sobre o quadrado, para indicar em que ponto teriam caído os dardos lançados.
Vamos criar uma função para gerar pi, dado um número n de lançamentos. Um algoritmo para isso poderia ser:
Gerar n coordenadas x e y aleatórias. Para cada coordenada, testar se está dentro do círculo. Se estiver, incrementar o contador de coordenadas dentro. Ao final, pi será igual a 4 ×(contagem dentro do círculo) ÷ n.
E como eu sei se uma coordenada está dentro do círculo? Ora, trigonometria básica. Qualquer ponto que tenha distância ao centro maior que 1 está fora do círculo. Se o centro do círculo está no ponto (0, 0), a distância de qualquer ponto interno ao círculo será menor que .
Note que como estamos trabalhando com quadrados, não importa se x ou y têm valores negativos. Você pode programar este cálculo com os comandos básicos de Python, se quiser, mas se olhar as funções disponíveis em math, verá que já existe a função hypot(x,y) que calcula exatamente isso.
Figura 4.7 Cálculo da distância dos pontos.
O programa vai retornar o valor de pi e o erro associado quando
comparado ao valor dado pelo módulo math. Com isso você vai ter uma boa ideia da exatidão do valor calculado por esse método.
Veja o Programa 4.14.
def calcula_pi(n):
“””Calcula pi e o erro associado a partir de n p
ontos.”””
return 0, 0
num = int(input(‘Quantos pontos devem ser sorteados?
‘))
pi, erro = calcula_pi(num)
print(‘Com’, num, ‘pontos, o valor de pi é’, pi, ‘co
m erro’, erro)
■ Programa 4.14: Programa com função vazia.
Achou algo estranho? É claro, o programa ainda não funciona. Escrevi-o dessa forma para mostrar-lhe um método de desenvolvimento de programas. Em vez de digitar todo o programa de uma vez, escrevi apenas seu esqueleto, ou seja, um esboço mínimo de que o programa precisa para funcionar. Apesar de não dar a resposta correta, este programa não tem erros de sintaxe e posso testar pelo menos a parte principal.
O programa principal vai ter uma pergunta ao usuário sobre quantos pontos deverão ser gerados. Lembrando que a função input() lê caracteres, de modo que precisamos usar int() para converter os caracteres em número. Em seguida é chamada a função que calcula o valor de pi e o erro associado. O que o programa retorna é um “tupla” com os dois valores. Na última linha, o programa imprime o que recebeu.
Dica 4.1 – Você não precisa ter o código completo desde o início.
Quando começar a programar, o principal é ter um plano, um algoritmo para resolver
o problema. Não se apegue demais à linguagem de programação, o risco de você ser
limitado por essa linguagem é grande.
Pense em um bom algoritmo e depois pesquise como implementá-lo com a
linguagem que você está usando.
Por exemplo, não é necessário saber como será feita a geração do número
aleatório desde o início. Se Python não fornecesse uma função para isso, você teria
que criar sua própria função, mas continuaria com seu algoritmo.
Uma solução algorítmica nasce como algo abstrato. A linguagem de
programação é o que vai fazê-la concreta.
É uma boa prática colocar um comentário antes da função explicando o que faz e o que devolve a quem a chamou. Não é obrigatório, mas serve para documentar sua função. Aqui usei uma docstring (veja Dica 4.2). Uma docstring é uma string que documenta os componentes de Python. Pode se estender por várias linhas. Por enquanto, mantenha apenas uma linha de documentação, com a informação direta sobre o que a função faz.
Dica 4.2 – Documente suas funções com docstring.
Python recomenda um sistema de documentação para seus componentes chamado
docstring. A recomendação de uso de docstring vale para as funções mais
importantes.
As funções que são usadas apenas internamente ou que sejam muito simples
poderiam ser documentadas usando apenas um comentário comum. Nada, porém,
impede você de usar a docstring em todas as funções.
Mas o que é uma docstring? Uma docstring é uma string de descrição de sua
função, logo após a linha do cabeçalho. Na realidade, docstring é usada em outras
situações, mas para nosso estágio de Python, basta usá-la no início da função.
Lembra da função hypot() de math? O que você obtém como ajuda da
função, chamando “help(math.hypot)” no ambiente interativo de
Python é o resultado de uma docstring.
Uma docstring de uma função fornece uma ajuda sobre o uso da função no
contexto dos programas.
Tecnicamente, uma docstring é delimitada por três aspas duplas e deve dizer o
que a função faz e não como faz. Ex:
“””Calcula pi e o erro associado a partir de n pontos.”””
Uma docstring errada seria:
“””Calcula pi pelo método de Monte Carlo, usando a probabilidade de um ponto
estar em um círculo .”””
Esta primeira descrição deve ser sucinta e caber em uma linha, sendo sempre
terminada por ponto, exclamação ou interrogação.
A sintaxe de docstring permite mais linhas para uma descrição mais completa,
mas, mesmo assim, nunca descrevendo como é realizada a função.
Detalhes do “como” podem ser escritos como comentários normais dentro do
código. Isso permite que se um dia você modi car o método de solução, a função
permanece com o mesmo cabeçalho.
Então, minha dica é: use a docstring para descrever sua função de forma direta e
sucinta. Acostume-se a sempre fazer isso. Ao longo do tempo você verá como este
hábito vai ajudá-lo a escrever programas melhores.
Por que escrever assim e não o programa inteiro direto? Da forma como está, o programa pode ser testado para que você veja se os dados foram entrados corretamente. Se houver erro, o programa pode ser consertado. É claro que a resposta do programa neste estágio será errada, mas não é o que importa neste momento. Só quero testar se a entrada de dados está correta e se a impressão do resultado não tem problemas. A execução do programa resulta em:
Quantos pontos devem ser sorteados? 12 Com 12 pontos, o valor de pi é 0 com erro 0
Perceba que a função está vazia, mas tem seus valores de retorno atribuídos. O valor zero serve apenas para testar. Se não der erro, podemos passar para a próxima fase.
Na próxima versão do nosso programa, vamos avançar, mas ainda não será a versão final.
1 import math
2
3 def gera_coordenadas_aleatorias():
4 “””Gera par de coordenadas aleatórias.”””
5 x = y = 0
6 return x, y
7
8 def coordenadas_dentro_circulo(x, y):
9 “””Testa se coordenadas estão dentro do círcul
o de raio 1.”””
10 return True
11
12 def calcula_pi(n):
13 “””Calcula pi e o erro associado a partir de
n pontos.”””
14 conta_circulo = 0
15 for i in range(n):
16 x, y = gera_coordenadas_aleatorias()
17 if coordenadas_dentro_circulo(x, y):
18 conta_circulo += 1
19 pi = 4 * conta_circulo / n
20 erro = math.fabs(pi - math.pi)
21 return pi, erro
22
23 num = int(input(‘Quantos pontos devem ser sortead
os? ‘))
24 pi, erro = calcula_pi(num)
25 print(‘Com’, num, ‘pontos, o valor de pi é’, pi,
‘com erro’,
26 erro)
■ Programa 4.15: Programa que calcula pi. Versão com math.
Avançamos, e agora a função calcula_pi() já começa a ter um
corpo. Foram acrescentadas duas funções: uma que gera as coordenadas aleatórias e outra que testa se as coordenadas estão dentro do círculo.
Repare na forma usada no teste para descobrir se a coordenada está dentro do círculo. A função que testa a coordenada deve retornar True ou False. Em geral os principiantes na programação gostam de um teste bem explícito:
if coordenadas_dentro_circulo(x, y) == True
Ora, esta construção é redundante. O if já testa se um valor é verdadeiro, então não precisa testar o retorno da função contra True. Veja que da forma como está escrito no programa, fica até mais legível. Você diz, em português:
“Se a coordenada está dentro do círculo...”
e não
“Se a coordenada que está dentro do círculo é igual à verdade”.
O módulo math foi necessário para a função math.fabs(), que devolve o valor absoluto de um número em ponto flutuante. Essa função seria facilmente codificada, mas uma rápida olhada na referência de math mostrou essa função. Por isso, lembre-se sempre de olhar a biblioteca de funções de um módulo antes de sair programando.
Neste ponto, a função gera_coordenadas_aleatorias() gera sempre a mesma coordenada (0,0) e o teste para saber se um ponto está dentro do círculo responde sempre que sim.
A execução deste programa resulta em: Quantos pontos devem ser sorteados? 12 Com 12 pontos, o valor de pi é 4.0 com erro 0.858407 3464102069
O programa continua dando o resultado errado. Ainda faltam duas funções para serem codificadas, mas podemos testar se pelo menos o que escrevemos até este ponto está sintaticamente correto. Até aqui tudo bem.
Para a versão final, precisamos ver como funciona a geração de números aleatórios em Python, por meio do módulo random.
A função random() gera um número randômico entre 0.0 e 1.0
(intervalo [0.0,1.0)). Perceba que essa faixa de valores não é bem aquilo de que precisamos, mas serve perfeitamente. Para nosso cálculo era preciso que gerássemos números aleatórios entre –1 e +1, mas random gera apenas entre 0 e 1. Mas, pense bem: números negativos importam neste caso? Não tem problema mudar seu plano original, se uma melhor opção se apresenta. O desenvolvimento de software é cheio de idas e vindas.
Para calcular a distância dos pontos ao centro vamos elevar ao
quadrado cada componente da coordenada cartesiana, então os números negativos desaparecem. Essa operação vai gerar pontos
apenas no quadrante superior direito. Olhando a Figura 4.8 você percebe que este quadrante corresponde a um quadrado de área igual a 1. Nesse caso, pegamos apenas 1/4 do círculo de raio 1 e área π, ou seja, voltamos à fórmula original que diz que a proporção dos pontos será de p/4.
import random as r
import math as m
def gera_coordenadas_aleatorias():
“””Gera par de coordenadas aleatórias.”””
x = r.random()
y = r.random()
return x, y
Figura 4.8 Quadrante com 200 pontos gerados aleatoriamente.
def coordenadas_dentro_circulo(x, y):
“””Testa se coordenadas estão dentro do círculo
de raio 1.”””
return m.hypot(x, y) < 1
def calcula_pi(n):
“””Calcula pi e o erro associado a partir de n p
ontos.”””
conta_circulo = 0
for i in range(n):
x, y = gera_coordenadas_aleatorias()
if coordenadas_dentro_circulo(x, y):
conta_circulo += 1
pi = 4 * conta_circulo / n
erro = m.fabs(pi - m.pi)
return pi, erro
num = int(input(‘Quantos pontos devem ser sorteados? ‘))
pi, erro = calcula_pi(num)
print(‘Com’, num, ‘pontos, o valor de pi é’, pi, ‘co
m erro’,
erro)
■ Programa 4.16: Programa que calcula pi. Versão final.
No Programa 4.16, mais duas novidades. As linhas iniciais:
import random as r
import math as m
dizem para importar os módulos random e math como r e m, respectivamente. Isso significa que podemos escrever as referências de maneira mais compacta. O módulo math passa a ser simplesmente m e random, r.
A função coordenadas_dentro_circulo() devolve um valor
True ou False (Verdadeiro ou Falso) de acordo com o resultado do teste, se o ponto está dentro do círculo ou não. Usa a função hypot() de Python com a coordenada do ponto (x, y), considerando o centro em (0, 0). Não é preciso fazer o cálculo e testar, possivelmente com o comando if, se o valor retornado por hypot() é menor que 1; basta devolver o próprio resultado do teste. Você poderia escrever a seguinte função:
def coordenadas_dentro_circulo(x, y):
“””Testa se coordenadas estão dentro do círculo
de raio 1.”””
distancia = m.hypot(x, y)
if distancia < 1:
return True
else:
return False
Se queremos apenas o resultado do teste, é redundante
executar tantas operações. Mesmo se fizéssemos o teste com o if, a opção else não seria necessária. Uma forma mais compacta poderia ser:
def coordenadas_dentro_circulo(x, y):
“””Testa se coordenadas estão dentro do círculo
de raio 1.”””
distancia = m.hypot(x, y)
if distancia < 1:
return True
return False
Percebeu a diferença? Se o comando if provoca o término da função, escrever a cláusula else é redundante. O fluxo de execução só chega ao comando após o if se o teste der falso. De qualquer modo, a melhor maneira de escrever a função coordenadas_dentro_circulo() é a apresentada no programa:
def coordenadas_dentro_circulo(x, y):
“””Testa se coordenadas estão dentro do círculo
de raio 1.”””
return m.hypot(x, y) < 1
Execute o programa escolhendo valores baixos e altos e veja quão bom é este método probabilístico. Por exemplo:
Quantos pontos devem ser sorteados? 500000 Com 500000 pontos, o valor de pi é 3.141168 com erro 0.0004246535897931558
Os resultados variam a cada vez que o programa é executado, e é assim mesmo, afinal este é um método probabilístico. O método de Monte Carlo serve para muitas outras situações. Tem aplicação em estudos de viabilidade econômica, análise de riscos, análise de ações, computação gráfica, geologia e muitas outras áreas.
EXERCÍCIO 4.12
Modi que o programa que calcula pi para perguntar diversas vezes pelo número
de pontos a serem sorteados, calculando pi para cada pedido. O programa deve
terminar quando for digitado ‘0’.
EXERCÍCIO 4.13