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.
01.import turtle
02. 
03.def pir_quadrados(n,posx, posy,lado):
04.    for i in range(1,n+1):
05.        # desenha linha i
06.        # posiciona
07.        turtle.penup()
08.        turtle.setx(posx+(n-i)*lado/2)
09.        turtle.pendown()     
10.        # desenha
11.        for j in range(1,i+1):
12.            quadrado(turtle.xcor(),turtle.ycor(), lado)
13.            turtle.setx(turtle.xcor()+lado)
14.        # muda de linha
15.        turtle.penup()
16.        turtle.goto(posx,turtle.ycor()-lado)
17.        turtle.pendown()
18.    turtle.hideturtle()
19.     
20.def quadrado(posx, posy,lado):
21.    turtle.showturtle()
22.    # posicioan
23.    turtle.penup()
24.    turtle.goto(posx, posy)
25.    turtle.pendown()
26.    # desenha
27.    for i in range(4):
28.        turtle.forward(lado)
29.        turtle.left(90)
30.    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!
01.def pir_quadrados_inv(n,posx, posy,lado):
02.    for i in range(n,0,-1): # <-- só alterámos aqui!!
03.        # desenha linha i
04.        # posiciona
05.        turtle.penup()
06.        turtle.setx(posx+ (n-i)*lado/2)
07.        turtle.pendown()     
08.        # desenha
09.        for j in range(1,i+1):
10.            quadrado(turtle.xcor(),turtle.ycor(), lado)
11.            turtle.setx(turtle.xcor()+lado)
12.        # muda de linha
13.        turtle.penup()
14.        turtle.goto(posx,turtle.ycor()-lado)
15.        turtle.pendown()
16.    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.
01.def triangulo(posx, posy, lado):
02.    turtle.showturtle()
03.    # posicioan
04.    turtle.penup()
05.    turtle.goto(posx, posy)
06.    turtle.pendown()
07.    # desenha
08.    for i in range(3):
09.        turtle.forward(lado)
10.        turtle.left(120)
11.    turtle.hideturtle()
E agora? Será que substituir o uso da definição quadrado pela definição de triângulo resolve a questão?
01.def pir_triangulos_inv(n,posx, posy,lado):
02.    for i in range(n,0,-1):
03.        # desenha linha i
04.        # posiciona
05.        turtle.penup()
06.        turtle.setx(posx+ (n-i)*lado/2)
07.        turtle.pendown()     
08.        # desenha
09.        for j in range(1,i+1):
10.            triangulo(turtle.xcor(),turtle.ycor(), lado) # <-- única mudança!!
11.            turtle.setx(turtle.xcor()+lado)
12.        # muda de linha
13.        turtle.penup()
14.        turtle.goto(posx,turtle.ycor()-lado)
15.        turtle.pendown()
16.    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:
01.def pir_hexagonos_inv(n,posx, posy,lado):
02.    for i in range(n,0,-1):
03.        # desenha linha i
04.        # posiciona
05.        turtle.penup()
06.        turtle.setx(posx+ (n-i)*lado/2)
07.        turtle.pendown()     
08.        # desenha
09.        for j in range(1,i+1):
10.            hexagono(turtle.xcor(),turtle.ycor(), lado) # <-- única mudança!
11.            turtle.setx(turtle.xcor()+lado)
12.        # muda de linha
13.        turtle.penup()
14.        turtle.goto(posx,turtle.ycor()-lado)
15.        turtle.pendown()
16.    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