segunda-feira, 5 de outubro de 2009
Python Wiki
Para os que gostam de Wikis é dar uma olhada em Python Wiki. Lá encontrará muita informação sobre a linguagem, aplicações e projectos que usam Python.
domingo, 4 de outubro de 2009
Visões
A linguagem Python tem um conjunto de módulos adicionais que permitem aumentar muito o seu poder expressivo. Hoje quero falar-vos de um bastante pequeno mas que permite brincar com coisas sérias. Baseia-se na ideia de uma tartaruga que se passeia sob nosso controlo, podendo deixar um rasto visível. Para isso tem uma caneta. Sempre que a caneta está em baixo e a tartaruga caminha, lá aparece o rasto. Existem por isso comandos para baixar a caneta, levantar a caneta, rodar para a esquerda ou para a direita, etc.
Existem variantes deste módulo. Quando instala o Python fica com a possibilidade de importar de imediato o módulo turtle. Outras possibilidades são o xturtle ou a sua versão mais completa cTurtle . É este último que iremos explorar.
Comecemos com um exemplo simples que nos permite desenhar um quadrado.
Executando este código obtém-se o desenho:

O código acima não é brilhante! Basta ver que repetimos 4 vezes o mesmo par de instruções. É por isso que existem estruturas de controlo repetitivas em todas as linguagens de alto nível. Para este caso em que o número de repetições é fixo e conhecido previamente vamos usar um ciclo for.
Alguns comentários. Em primeiro lugar, usamos a notação normal para o uso de funções que estão definidas num módulo que foi importato com import módulo. Em segundo lugar, o mesmo comando pode ter mais do que um nome. Assim, por exemplo,podemos abreviar forward por fd.Em terceiro lugar, existem neste programa quatro comandos, cujos nomes em inglês são claros.
Continuemos esta breve exploração. Agora vamos desenhar um pentágono, um polígono regular de 5 lados. Com pouco esforço chegamos ao seguinte código:
Executado o código obtemos agora a figura:

Fixemo-nos nos dois programas. Quais são as suas únicas diferenças? Claro: o valor do número de lados (repetições) e o ângulo. Mas há alguma relação entre eles? Claro que sim, uma vez mais. Num polígono regular o produto entre o número de lados e o ângulo interno dá sempre 360 graus. Com base nisso podemos generalizar o código, escrevendo um programa que dá para qualquer polígono regular.
Pode experimentar.
Mas será que só dá para isto? Claro que não. Isto é apenas um começo. Quando dominar o módulo vai ser capaz de desenhar um linda árvore inclinada para a esquerda devido ao vento.

Existem variantes deste módulo. Quando instala o Python fica com a possibilidade de importar de imediato o módulo turtle. Outras possibilidades são o xturtle ou a sua versão mais completa cTurtle . É este último que iremos explorar.
Comecemos com um exemplo simples que nos permite desenhar um quadrado.
import cTurtle
def quadrado(lado):
"""
Desenha um quadrado.
"""
cTurtle.showturtle()
#-----------
cTurtle.fd(lado)
cTurtle.rt(90)
cTurtle.fd(lado)
cTurtle.rt(90)
cTurtle.fd(lado)
cTurtle.rt(90)
cTurtle.fd(lado)
cTurtle.rt(90)
#----------
cTurtle.hideturtle()
quadrado(100)
Executando este código obtém-se o desenho:

O código acima não é brilhante! Basta ver que repetimos 4 vezes o mesmo par de instruções. É por isso que existem estruturas de controlo repetitivas em todas as linguagens de alto nível. Para este caso em que o número de repetições é fixo e conhecido previamente vamos usar um ciclo for.
import cTurtle
def quadrado(lado):
"""
Desenha um quadrado.
"""
cTurtle.showturtle()
#-----------
for i in range(4):
cTurtle.fd(lado)
cTurtle.rt(90)
#----------
cTurtle.hideturtle()
quadrado(100)
Alguns comentários. Em primeiro lugar, usamos a notação normal para o uso de funções que estão definidas num módulo que foi importato com import módulo. Em segundo lugar, o mesmo comando pode ter mais do que um nome. Assim, por exemplo,podemos abreviar forward por fd.Em terceiro lugar, existem neste programa quatro comandos, cujos nomes em inglês são claros.
Continuemos esta breve exploração. Agora vamos desenhar um pentágono, um polígono regular de 5 lados. Com pouco esforço chegamos ao seguinte código:
import cTurtle
def pentagono(lado):
"""
Desenha um pentágono.
"""
cTurtle.showturtle()
#-----------
for i in range(5):
cTurtle.fd(lado)
cTurtle.rt(72)
#----------
cTurtle.hideturtle()
pentagono(100)
Executado o código obtemos agora a figura:

Fixemo-nos nos dois programas. Quais são as suas únicas diferenças? Claro: o valor do número de lados (repetições) e o ângulo. Mas há alguma relação entre eles? Claro que sim, uma vez mais. Num polígono regular o produto entre o número de lados e o ângulo interno dá sempre 360 graus. Com base nisso podemos generalizar o código, escrevendo um programa que dá para qualquer polígono regular.
import cTurtle
def poligono_regular(comp_lado,num_lados):
"""
Desenha um polígono.
"""
cTurtle.showturtle()
angulo_viragem = 360 /num_lados
#-----------
for i in range(num_lados):
cTurtle.forward(comp_lado)
cTurtle.right(angulo_viragem)
#----------
cTurtle.hideturtle()
poligono_regular(50,8)
Pode experimentar.
Mas será que só dá para isto? Claro que não. Isto é apenas um começo. Quando dominar o módulo vai ser capaz de desenhar um linda árvore inclinada para a esquerda devido ao vento.

sábado, 3 de outubro de 2009
Problema 1.20
Neste problema é-nos pedido um programa que teste se um dado inteiro é primo. Um número diz-se primo se os únicos divisores são ele próprio e o número 1. Este exemplo dá-nos a oportunidade de mostrar como as definições nos permitem usar a abstracção e estruturar o código. Na nossa abordagem começamos com a definição:
Como se vê pela solução apresentada (linha 2) delegamos noutra definição, divisores_c(n), a contagem dos divisores. Se esse número for 2 será primo, caso contrário não será. Claro que o programa estará correcto apenas se forem incluídos 1 e o próprio número na contagem, como na solução seguinte.
Esta solução baseia-se no uso do padrão acumulador. O acumulador do resultado conta é inicializado na linha 5 e actualizado na linha 8. Visualmente temos a seguinte dependência entre as duas definições:

Mas admitamos que queremos optimizar o programa que conta os divisores deixando de fora o caso óbvio dos divisores próprios. Podemos então optar por:
Só precisámos mexer na linha 6 do código! Agora necessitamos alterar o nosso programa principal.
E já está!
def primo(n):
conta = divisores_c(n)
if conta == 2:
return True
else:
return False
Como se vê pela solução apresentada (linha 2) delegamos noutra definição, divisores_c(n), a contagem dos divisores. Se esse número for 2 será primo, caso contrário não será. Claro que o programa estará correcto apenas se forem incluídos 1 e o próprio número na contagem, como na solução seguinte.
def divisores_c(n):
"""
Conta os divisores de um número.
"""
conta = 0
for i in range(1, n+1):
if (n % i) == 0:
conta = conta + 1
return conta
Esta solução baseia-se no uso do padrão acumulador. O acumulador do resultado conta é inicializado na linha 5 e actualizado na linha 8. Visualmente temos a seguinte dependência entre as duas definições:

Mas admitamos que queremos optimizar o programa que conta os divisores deixando de fora o caso óbvio dos divisores próprios. Podemos então optar por:
def divisores_c(n):
"""
Conta os divisores de um número.
"""
conta = 0
for i in range(2, n/2 + 1):
if (n % i) == 0:
conta = conta + 1
return conta
Só precisámos mexer na linha 6 do código! Agora necessitamos alterar o nosso programa principal.
def primo(n):
conta = divisores_c(n)
if conta == 0:
return True
else:
return False
E já está!
Problema 1.19
Este problema é simples e consiste em determinar os divisores de um número inteiro n. Todos sabemos o que são divisores de um número: x diz-se divisor de y se o resto da divisão de y por x for zero. Passar da definição ao código é trivial.
O ciclo for (início na linha 5) vai gerando em sequência os candidatos a divisores. Na linha 6 a instrução condicional if testa e filtra os que são divisores.
Porque é que, na linha 5 range tem como argumentos 1 e n+1? Bom, a resposta é: porque serão gerados números de 1 a n, devido ao modo como range funciona. Mas não é a melhor solução! Melhor seria usar range(1,n/2). Porquê? Na linha 6, temos o início da instrução condicional mais simples de todas com a sintaxe:
Podem também existir condicionais de duas vias, como em:
ou mesmo de múltiplas vias:
A semântica desta última construção é a seguinte: percorremos de cima para baixo as condições, e executamos o código da primeira cujo resultado for True. Não havendo nenhuma condição verdadeira executa o bloco associado ao else.
def divisores(n):
"""
Mostra divisores de um número.
"""
for i in range(1,n+1):
if (n % i) == 0:
print i
O ciclo for (início na linha 5) vai gerando em sequência os candidatos a divisores. Na linha 6 a instrução condicional if testa e filtra os que são divisores.
Porque é que, na linha 5 range tem como argumentos 1 e n+1? Bom, a resposta é: porque serão gerados números de 1 a n, devido ao modo como range funciona. Mas não é a melhor solução! Melhor seria usar range(1,n/2). Porquê? Na linha 6, temos o início da instrução condicional mais simples de todas com a sintaxe:
if [condição]:
[bloco]
Podem também existir condicionais de duas vias, como em:
if [condição]:
[bloco_sim]
else:
[bloco_não]
ou mesmo de múltiplas vias:
if [condição_1]:
[bloco_1]
elif [condição_2]:
[bloco]_2
...
else:
[bloco_n]
A semântica desta última construção é a seguinte: percorremos de cima para baixo as condições, e executamos o código da primeira cujo resultado for True. Não havendo nenhuma condição verdadeira executa o bloco associado ao else.
Entradas e Saídas
Dividir para reinar é um velho princípio de resolução de problemas. Em programação isso significa usar mecanismos de abstracção que promovam, entre outras coisas, a clareza, a economia e a reutilização de código. As linguagens de programação de alto nível todas possuem uma forma de concretizar esses objectivos. Chamam-se funções. Conhecemos a noção equivalente da matemática: uma correspondência que estabelecemos entre objectos de dois conjuntos. Por exemplo, a função raiz quadrada: dado um número devolve o correspondente valor da raiz quadrada. Em Python a definição de uma função obedece à sintaxe seguinte:
def raiz_2(n,aprox):
"""
Calcula o valor aproximado da raiz quadrada de n.
"""
x= n/2.0
for i in range(aprox):
x = (1/2.0) * (x + (n/x))
return x
Na linha 1 temos o cabeçalho da definição: formado pela palavra reservada def, seguido do nome da definição, seguido da lista separada por vírgulas dos parâmetros de entrada, seguido dos dois pontos. Os dois pontos em Python marcam o início de um novo bloco de código, devendo as instruções seguintes estarem indentadas. Deste modo, temos uma forma natural de introduzir os dados através dos parâmetrosda definição, e um modo de devolver o resultado através do comando return(linha 8). Mas em programação existem outros modos de comunicar dados e extrair resultados: input() ou raw_input(), parta introduzir os dados, print, para comunicar resultados. Dadas estas 4 situações são possíveis (quase) todas as combinações possíveis. A opção depende do problema em concreto. Para concluir: uma coisa é definir uma função. Se apenas fizesse-mos isso nada ganharíamos. É preciso também usar a definição.
print raiz_2(2,20)
Quando usamos uma nova função é feita associação entre os objectos que passamos como argumentos (neste caso, 2 e 20) e os nomes dos parâmetros (neste caso n e aprox). Esta associação mantém-se durante a activação e no contexto da definição. Em síntese, o argumento passa ao parâmetro a sua identidade (referência).
quinta-feira, 1 de outubro de 2009
Pi
Todos já ouvimos falar de números irracionais. O número pi é um deles. Representa o quociente entre o perímetro de uma circunferência e o seu diâmetro. Existem vários modos de obter o valor aproximado de pi. Arquimedes, Leibniz, Wallis, propuseram diferentes soluções. Hoje vou falar de mais uma. Foi proposta por Steven Strogatz no seu livro The calculus of friendship (Princeton University Press - 2009). A fórmula para o cálculo é a seguinte:

Quais são os diferentes factores que aparecem à direita da equação? Bom, basta atribuir diferentes valores a k e ver o que se passa:

Não me parece que tenhamos avançado muito, ou avançámos??? Façamos umas pequenas manipulações e chegamos a:

E chegamos a um resultado fantástico: só precisamos de calcular raízes quadradas!!! A partir daqui é tudo mais fácil. Basta ver que cada factor no produto se pode obter a partir do anterior. Pense nisso antes de prosseguir a leitura. Fazemos um pequeno programa em Python e já está!
import math
def pi_strogatz(n):
"""
Calculo do valor aproximado de pi pelo
método de Steven Strogatz.
"""
acum = 1
termo = 0
for i in range(n):
termo = math.sqrt(2 + termo)
acum = acum * termo / 2
return 2 / acum
Aprender a programar
Aprender a programar tem tanto de método, rigor, disciplina, como de arte. É algo que se vem discutindo desde sempre. Recordo o grande debate dos anos 70 (do século passado...) sobre o uso da instrução GO TO, e como isso originou a denominada programação descendente e o aparecimento da disciplina de Metodologia da Programação. Mas para mim, depois de muitos anos a tentar formalismos, acho que a melhor maneira de aprender a programar é pegar num problema, num ambiente de desenvolvimento e numa linguagem e ... programar a solução. Mas podemos também aprender com pequenas ferramentas interactivas. Por exemplo, no caso da linguagem Python, existe um sítio (http://www.trypython.org) onde se pode aprender a linguagem a partir de um navegador (browser). Give it a try!
Subscrever:
Mensagens (Atom)
