
Para acompanhar este livro, escrevi um conjunto de módulos chamados Swampy. Um desses módulos é TurtleWorld, que fornece um conjunto de
funções para desenhar linhas através de orientações dadas a tartarugas para que caminhem na tela. Você pode baixar Swampy em thinkpython.com/swampy.
Siga as instruções para instalar Swampy em seu sistema.
Vá até o diretório ou pasta que contém o arquivo TurtleWorld.py e crie um novo arquivo chamado polygon.py, digitando nele e o seguinte código:
from TurtleWorld import *
world = TurtleWorld()
bob = Turtle()
print bob
wait_for_user()
A primeira linha é uma variação da declaração import que já vimos. Ao invés de criar um objeto módulo esta declaração
importa as funções diretamente do módulo para que possam ser acessadas sem o uso da notação de ponto.
As linhas seguintes criam um TurtleWorld atribuído ao world e uma Turtle atribuído a bob. Impressão de
bob resultará em algo como:
<TurtleWorld.Turtle instance at 0xb7bfbf4c>
Isso significa que bob se refere a uma instância de uma Turtle (tartaruga), tal como definido no módulo TurtleWorld.
Neste contexto, "instância" significa um membro de um conjunto; esta Turtle é um dos elementos de todos os objetos Turtle possíveis.
wait_for_user é uma função usada para informar a TurtleWorld que espere até que o
usuário faça alguma coisa, fornecendo comandos. TurtleWorld fornece diversas funções de manipulação de direção: fd
e bk para frente e para trás, lt e rt para virar à esquerda ou direita (em inglês, fd: forward,
bk: back, lt: left e rt: right). Além disso, cada tartaruga segura uma caneta que pode estar abaixada ou não. Se estiver para baixo a tartaruga deixa um rastro quando
ele se move. As funções pu e pd significam "caneta para cima" e "caneta para baixo" (em inglês, pen up e pen down).
Para desenhar um ângulo reto, adicione estas linhas no programa (após a criação de bob e antes de chamar wait_for_user):
fd(bob, 100)
rt(bob)
fd(bob, 100)
A primeira linha manda bob dar 100 passos a frente. A segunda manda virar à esquerda. Ao executar este programa você deverá
ver bob se mover na direção leste e sul, deixando para trás dois segmentos de linha.
Em seguida modifique o programa para desenhar um quadrado. Não prossiga até que tenha conseguido!
É provável que você tenha escrito algo assim (deixando de fora o código que cria TurtleWorld e espera para o usuário):
fd(bob, 100)
lt(bob)
fd(bob, 100)
lt(bob)
fd(bob, 100)
lt(bob)
fd (bob, 100)
Este código está correto e realizará a tarefa proposta. No entanto, podemos fazer a mesma coisa de forma mais concisa usando a declaração for.
Experimente criar um script, ou simplesmente usar o comando de linha, com o seguinte código:
for i in range(4):
. . . . print 'Olá!'
# Isto produzirá a saída:
Olá!
Olá!
Olá!
Olá!
Este é o uso mais simples da declaração for. Veremos outros usos desta declação mais tarde. Agora você deverá ser capaz de reescrever seu programa para desenhar um quadrado.
Por exemplo, aqui está uma solução para este problema:
for i in range(4):
fd(bob, 100)
lt (bob)
A sintaxe de uma declaração for é semelhante a de uma definição de função. Ele tem um cabeçalho que termina com dois pontos e um corpo recuado. O
corpo pode conter qualquer número de declarações. A declaração for é as vezes chamada de loop, porque o fluxo de execução
passa através do corpo e volta ao início dando voltas (loops). Neste caso, corre-se o corpo quatro vezes.
Na verdade esta última versão é um pouco diferente do código de desenho anterior porque, depois de desenhar o último lado do quadrado ele ainda muda a direção da tartaruga. Por isto ela leva um pouco mais de tempo que a versão anterior mas simplifica bastante o código. Além disto ela deixa a tartaruga na mesma na posição em que estava na partida.
Seguem uma série de exercícios utilizando o TurtleWorld. Embora divertidos eles têm um propósito. Pense sobre este propósito enquanto você estiver trabalhando com eles.
As soluções para os exercícios estão nas seções seguintes. Não olhe até que você tenha completado (ou pelo menos tentado) suas próprias soluções .
quadrado que recebe um parâmetro denominado t, que é uma Turtle. A função deve usar a tartaruga para desenhar um quadrado.
bob como argumento para quadrado, e executar o programa novamente.quadrado, chamado comprimento. Modifique o corpo da função de modo que comprimento
seja o comprimento dos lados. Depois modifique a chamada de função para fornecer este argumento. Execute o programa novamente e teste seu programa para diversos valores de comprimento.lt e rt fazem giros de 90 graus, por default. Você pode acrescentar um segundo argumento para especificar o número de graus. Por exemplo, lt(bob, 45)
para girar bob 45 graus para a esquerda.quadrado e mude o nome para poligono. Adicione outro parâmetro denominado n
e modifique a função para que ela desenhe um polígono regular de n lados. Dica: Os ângulos externos de um polígono regular de n lados são 360.0/n graus.circulo que tem uma tartaruga t e o raio r como parâmetros
e que desenha um círculo aproximado chamando poligono com um comprimento e número de lados adequados. Teste
a sua função com diversos valores para r. comprimento * n = circunferencia.
bob estiver muito lento, você pode apressá-lo alterando bob.delay, que é o tempo entre movimentos, em segundos. Um valor de bob.delay = 0,01
deve fazê-lo movimentar-se a contento.circulo chamando-a de arco, que recebe o parâmetro adicional angulo para determinar que
fração de um círculo deve ser desenhada. angulo pode ser fornecida em graus de forma que um círculo completo será desenhado quando angulo = 360.O primeiro exercício pede que você coloque o código para desenhar quadrados dentro de uma função e depois chame a função, passando a tartaruga como parâmetro. Aqui está uma solução:
def quadrado(t):
for i in range(4):
fd(t, 100)
lt(t)
quadrado(bob)
As declarações fd e lt, dentro do loop for são recuadas duas vezes, indicando que
fazem parte do loop que, por sua vez, está dentro da definição da função. A linha seguinte quadrado(bob), está alinhada com a
margem esquerda, representando o final do loop e da definição da função.
Dentro da função, t se refere à mesma tartaruga que bob, portanto as
declarações lt(t) e lt(bob) tem o
mesmo efeito. Por que então não chamar o parâmetro de bob?
A idéia aqui é que t pode ser qualquer tartaruga, e não apenas bob. Você pode
criar outras tartarugas e passá-las como argumento para quadrado:
ray = Turtle()
quadrado(ray)
A prática de envolver um pedaço de código dentro de uma função é chamada de encapsulamento. Uma das vantagens do encapsulamento é que ele atribui um nome para aquele porção de código, o que funciona como um tipo de documentação. Outra vantagem está na reutilização do código. Um bloco de código pode ser chamado várias vezes sem que se precise copiá-lo várias vezes!
O próximo passo foi adicionar o parâmetro comprimento na função quadrado. Aqui está uma solução:
def quadrado(t, comprimento):
for i in range(4):
fd(comprimento, t)
lt(t)
quadrado(bob, 100)
Este ato de adicionar um parâmetro em uma função é chamado de generalização, pois faz com que a função fique mais geral: na versão anterior, o quadrado tem sempre o mesmo tamanho; nesta versão ele pode ser de qualquer tamanho.
O passo seguinte também é uma generalização. Em vez de desenhar quadrados, poligonos desenha polígonos regulares com qualquer número de lados. Eis aqui uma solução:
def poligono(t, n, comprimento):
angulo = 360.0/n
for i in range(n):
fd(t, comprimento)
lt(t, angulo)
poligono(bob, 7, 70)
Isso faz com que seja desenhado um polígono de 7 lados com comprimento 70. Observe que, uma função com muitos argumentos, acaba sendo fácil esquecer quais são e em que ordem elas devem estar estes argumentos. Em Python é possível usar argumentos nomeados, (keyword arguments) incluindo, na definição da função, os nomes dos parâmetros:
poligono(bob, n = 7, comprimento = 70)
Esta sintaxe torna o programa mais legível e serve como lembrete do modo como argumentos e parâmetros funcionam: quando você chama uma função os argumentos são atribuídos aos parâmetros.
O próximo passo é escrever circulo, que tem um raio r como um parâmetro. Aqui está uma solução simples que usa
poligonos para desenhar um polígono de 50 lados:
def circulo(t, r):
circunferencia = 2 * math.pi *r
n = 50
comprimento = circunferencia/n
poligono(t, n, comprimento)
A primeira linha calcula a circunferência de um círculo com raio
r usando a fórmula 2 π r. Como usamos math.pi, temos
que importar o módulo de funções matemáticas (import math).
Por convenção as declarações import são normalmente colocadas no início
do script.
n é o número de segmentos de linha em nossa aproximação de um círculo, enquanto comprimento
é o comprimento de cada segmento. Assim, poligonos desenha um polígono de 50 lados que se aproxima de um círculo com raio r.
Esta solução tem a seguinte limitação: n é uma constante e isto significa que, para círculos muito grandes, os
segmentos de linha são muito longos, e para círculos pequenos se desperdiça muito tempo desenhando segmentos pequenos. Uma
solução seria a de generalizar a função tomando n como um parâmetro. O usuário (qualuer um que use circulo
) teria mais controle mas a interface ficaria um pouco menos limpa.
A interface de uma função é um resumo de como ela é usada: quais são os parâmetros, o que ela faz e qual é o valor de retorno. Uma interface é limpa "clean" se é "tão simples como possível, mas não mais simples que isto. (Einstein)"
Neste exemplo r faz parte da interface pois especifica o círculo a ser desenhado. n seria menos adequado como parâmetro pois se refere aos detalhes de como o círculo deve ser desenhado.
Ao invés complicar a interface é melhor escolher um valor apropriado de n dependendo do valor de circunferencia:
def circulo(t, r):
circunferencia = 2 * math.pi * r
n = int(circunferencia/3) + 1
comprimento = circunferencia/n
poligono(t, n, comprimento)
Agora o número de segmentos é (aproximadamente) circunferência/3, de forma que o comprimento de cada segmento é (aproximadamente) 3. Este
comprimento é suficientemente pequeno para que os círculos tenham boa aparência, mas é grande o bastante para ser eficiente e adequado para círculos de qualquer tamanho.
Depois de escrever a função circulo nós a reutilizamos para poligonos
porque um polígono de muitos lados é uma boa aproximação para um círculo. Mas a função arco não se mostra tão cooperativa. Não podemos usar poligono
ou circulo para desenhar um arco.
Uma alternativa é começar com uma cópia do poligono e transformá-lo em arco. O resultado pode ser como este:
def arco(t, r, angulo):
arc_length = 2 * math.pi * r * angulo / 360
n = int (arc_length / 3) + 1
step_length = float(angulo)/n
for i in range(n):
fd(t, step_length)
lt(t, step_angle)
A segunda metade desta função se parece com polígono, mas não podemos reutilizar poligonos
sem alterar a interface. Poderíamos generalizar poligono inserindo um ângulo como terceiro argumento mas, neste caso o nome poligono
já não seria apropriado! Em vez disso, vamos criar uma função mais geral e chamá-la polilinha:
def polilinha(t, n, comprimento, angulo):
for i in range(n):
fd(t, comprimento)
lt(t, angulo)
Agora podemos reescrever poligono e arco para usar polilinha:
def poligono(t, n, comprimento):
angulo = 360.0/n
polilinha(t, n, comprimento, angulo)
def arco(t, r, angulo):
arc_length = 2 * math.pi * r * angulo/360
n = int(arc_length / 3) + 1
step_length = arc_length/n
step_angle = float (angulo)/n
polilinha(t, n, step_length, step_angle)
Finalmente, podemos reescrever circulo para usar arco :
def circulo(t, r):
arco(t, r, 360)
Este processo de reorganização de um programa para melhorar as interfaces de funções e facilitar a reutilização de código é chamado de
refatoring. Neste caso notamos que havia um código semelhante em arco e
poligono, de modo que "fatorado" em polilinha.
Se tivéssemos planejado com antecedência poderíamos ter escrito polilinha diretamente e evitar o refactoring. Mas, muitas vezes, não sabemos o
suficiente no início de um projecto para projetar todas as interfaces. Na medida em que se avança na programação o problema se torna mais claro. Às vezes o refactoring
simplesmente indica que você aprendeu alguma coisa nova sobre o problema que quer resolver.
Um plano de desenvolvimento é um processo para escrever programas. O processo que utilizamos neste estudo de caso foi de "encapsulamento e generalização." As etapas deste processo são:
Esse processo tem alguns inconvenientes, e vamos ver alternativas mais tarde. Mas ele pode ser útil se você não sabe, à princípio, como dividir o programa em funções. Esta abordagem permite realizar um projetar e aperfeiçá-lo na medida em que você o desenvolve.
Uma docstring é uma sequência no início de uma função que explica a interface ("doc" é a abreviação de "documentação"). Aqui está um exemplo:
def polilinha(t, comprimento, n, angulo):
"""Desenha n segmentos de retas com comprimento dado, formando angulo (em graus) entre eles.
t é um objeto (Turtle)
"""
for i in range(n):
fd(t, comprimento)
lt(t, angulo)
Esta docstring é uma string entre aspas triplas - também conhecida como string multilinha porque as tês aspas permitem que a sequência de caracteres (string) ocupe mais de uma linha. A docstring é bastante resumida mas contém a informação essencial para o uso desta função. Ela explica sucintamente o que a função faz (sem entrar em detalhes de como faz), que efeito cada parâmetro tem sobre o comportamento da função e de que tipo cada parâmetro deve ser (se isto não for óbvio).
Escrever esse tipo de documentação é uma parte importante do design de interface. Uma interface bem projetada deve ser simples de explicar: se você tem dificuldade em explicar uma função, este pode ser um sinal de que a interface deveria ser melhorada.
Uma interface é como um contrato entre uma função e aquele que a chama. Quem chama, pode ser o usuário ou outra parte do programa, concorda em fornecer alguns parâmetros e a função aceita fazer o trabalho certo.
Por exemplo, polilinha requer quatro argumentos. A primeira tem de ser uma Turtle ou algum outro objeto que aceita as
funções fd e lt . O segundo é um número que provavelmente deve ser positivo,
apesar de se verificar que a função funciona mesmo se for negativo. O terceiro argumento deve ser um inteiro pois a função range
retorna um erro de seu argumento não for um inteiro (dependendo de qual versão de Python você está executando). O quarto tem que ser um número,
compreendido como a medida do ângulo, em graus.
Estes requisitos são chamados de pré-condições, pois devem estar satisfeitos antes que a função inicie sua execução. Por outro lado, as condições quanto ao resultado da função são chamados de pós-condições. Entre estas condições posteriores estão o efeito que se deseja obter com aquela função (tal como desenhar segmentos de reta) e eventuais efeitos colaterais (como mover a Turtle ou fazer outras alterações no TurtleWorld).
Pré-condições são de responsabilidade do solicitante. Se ele viola uma condição (que se espera esteja devidamente documentada!) e a função não funciona corretamente, o erro está na chamada, e não na função.
poligono, arco e circulo.circulo(bob, ray). Você
pode fazer os cálculos a mão ou acrescentar declarações print ao longo do código para ver resultados parciais.arco no ponto 4,7 não é muito precisa porque a aproximação linear do círculo é sempre
fora do círculo verdadeiro. Como resultado, a tartaruga acaba por algumas unidades de distância do destino correto. Minha solução mostra uma maneira de reduzir o efeito
desse erro. Leia o código e ver se faz sentido para você. Se você desenhar um diagrama, você pode ver como ele funciona.Escreva um conjunto geral de funções capazes de desenhar flores como as mostradas na figura 1:

Escreva um conjunto geral de funções capazes de desenhar formas como estas, na figura 2:

As letras do alfabeto podem ser construídas a partir de um pequeno número de elementos básicos, como linhas verticais e horizontais e algumas curvas. Projete uma fonte que pode ser desenhada com um número mínimo de elementos básicos. Depois escreva funções para desenhar as letras do alfabeto.
Escreva uma função para cada letra, com nomes draw_a, draw_b, etc, e coloque suas funções
em um arquivo chamado letters.py. Você pode baixar uma "máquina de escrever tartaruga" para ajudá-lo a testar o código.
Você pode ver as soluções dadas pelo autor nas seguintes páginas:
Leia no site: | Devemos Acreditar na Ciência? | Hipótese, Modelo e Teoria em Física | Cosmologia - Estrutura do Universo | História da Pessoa com Deficiência |