segunda-feira, 11 de novembro de 2013

Ai, é tão parecido com...(III)

Regressemos às nossas pirâmides. Recordo aqui o programa para desenhar uma pirâmide de quadrados.
import turtle

def pir_quadrados(n,posx, posy,lado):
    for i in range(1,n+1):
        # desenha linha i
        # posiciona
        turtle.penup()
        turtle.setx(posx+(n-i)*lado/2)
        turtle.pendown()      
        # desenha
        for j in range(1,i+1):
            quadrado(turtle.xcor(),turtle.ycor(), lado)
            turtle.setx(turtle.xcor()+lado)
        # muda de linha
        turtle.penup()
        turtle.goto(posx,turtle.ycor()-lado)
        turtle.pendown()
    turtle.hideturtle()
    
def quadrado(posx, posy,lado):
    turtle.showturtle()
    # posicioan
    turtle.penup()
    turtle.goto(posx, posy)
    turtle.pendown()
    # desenha
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.hideturtle()
Quando executado para um pirâmide de tamanho 4, com 20 pixeis de lado obtemos:
Admitamos que o problema agora é semelhante com a diferença da pirâmide dever aparecer... invertida.
Como fazer? olhando para o código acima percebemos que ele está feito para que na linha 1 seja desenhado 1 quadrado, na linha 2, 2 quadrados e, de um modo geral, na linha i sejam desenhados i quadrados. Este procedimento é controlado pelos dois ciclos for. O mais externo identifica a linha i e o mais interno garante que são desenhados i quadrados. Mas agora o que pretendemos é que na linha 1 sejam desenhados n, na 2 (n-1), ..., e na n apenas 1. Se não queremos mexer muito no código basta o ciclo exterior comece por identificar a linha n, depois a (n-1) , etc. Com esta única alteração o problema fica resolvido!
def pir_quadrados_inv(n,posx, posy,lado):
    for i in range(n,0,-1): # <-- só alterámos aqui!!
        # desenha linha i
        # posiciona
        turtle.penup()
        turtle.setx(posx+ (n-i)*lado/2)
        turtle.pendown()      
        # desenha
        for j in range(1,i+1):
            quadrado(turtle.xcor(),turtle.ycor(), lado)
            turtle.setx(turtle.xcor()+lado)
        # muda de linha
        turtle.penup()
        turtle.goto(posx,turtle.ycor()-lado)
        turtle.pendown()
    turtle.hideturtle() 
Fantástico não é? Mas suponhamos que alguém tem algo contra quadrados e prefere triângulos?? Bem, temos que ser capazes de desenhar triângulos, certo? Vamos a isso.
def triangulo(posx, posy, lado):
    turtle.showturtle()
    # posicioan
    turtle.penup()
    turtle.goto(posx, posy)
    turtle.pendown()
    # desenha
    for i in range(3):
        turtle.forward(lado)
        turtle.left(120)
    turtle.hideturtle()
E agora? Será que substituir o uso da definição quadrado pela definição de triângulo resolve a questão?
def pir_triangulos_inv(n,posx, posy,lado):
    for i in range(n,0,-1):
        # desenha linha i
        # posiciona
        turtle.penup()
        turtle.setx(posx+ (n-i)*lado/2)
        turtle.pendown()      
        # desenha
        for j in range(1,i+1):
            triangulo(turtle.xcor(),turtle.ycor(), lado) # <-- única mudança!!
            turtle.setx(turtle.xcor()+lado)
        # muda de linha
        turtle.penup()
        turtle.goto(posx,turtle.ycor()-lado)
        turtle.pendown()
    turtle.hideturtle() 
Então e não é que resolve?
Mas porque funciona? É facil de perceber que a estrutura global é a mesma. Por outro lado, o posicionamento de cada triângulo obedece à mesma regra do caso dos quadrados. Entusiasmados, passamos a hexágonos:
def pir_hexagonos_inv(n,posx, posy,lado):
    for i in range(n,0,-1):
        # desenha linha i
        # posiciona
        turtle.penup()
        turtle.setx(posx+ (n-i)*lado/2)
        turtle.pendown()      
        # desenha
        for j in range(1,i+1):
            hexagono(turtle.xcor(),turtle.ycor(), lado) # <-- única mudança!
            turtle.setx(turtle.xcor()+lado)
        # muda de linha
        turtle.penup()
        turtle.goto(posx,turtle.ycor()-lado)
        turtle.pendown()
    turtle.hideturtle() 
Quando executamos o código, a surpresa. Não funciona:
A razão está no modo como vamos avançando os hexágonos em cada linha e na forma como baixamos de linha. Então quais os valores certos? olhemos para a figura:
Ela diz-nos que dentro de cada linha temos que avançar x do valor do lado mais duas vezes o valor de B. Quando mudamos de linha temos que alterar y de um valor igual a A. Mas o que são A e B? Um pouco de geometria diz-nos que A = 2* coseno(30) e B = coseno(60). Feitas as contas, chegamos a A= 1.732* lado e B = 0.5 * lado. Daí a nossa solução final: def pir_hexagonos(n,posx, posy,lado): for i in range(n,0,-1): # desenha linha i # posiciona turtle.penup() turtle.setx(posx+(n-i)*lado) turtle.pendown() # desenha for j in range(1,i+1): hexagono(turtle.xcor(),turtle.ycor(), lado) turtle.penup() turtle.setx(turtle.xcor()+2*lado) # <-- Novidade! turtle.pendown() # muda de linha turtle.penup() turtle.goto(posx,turtle.ycor()-1.732*lado) # <-- Novidade! turtle.pendown() turtle.hideturtle() Ver para crer:
Moral da história. Existem padrões de programação. Para uma dada classe de problemas semelhantes as soluções também elas são semelhantes. De qualquer modo temos que ter cuidado com generalizações apressadas e identificar bem o que faz a essência de uma solução. Neste caso o modo como se posiciona a tartaruga para uma linha e dentro de uma dada linha.

Agora, pode tentar com ... pentágonos??

Sem comentários:

Enviar um comentário