quarta-feira, 16 de outubro de 2013

Olhar e ver (II) ou um farol sem faroleiro...

Acontecem coisas fantásticas nos testes como, por exemplo ter que desenhar um lindo farol como o da figura.
Quando olhamos para o problema diante de nós ele parece muito complexo. Então para melhor dominar a complexidade vamos dividir o nosso problema em sub-problemas e vamos começar por esquecer alguns detalhes. Depois, com calma, havemos de chegar ao resultado pretendido, ou mesmo superar o que era pedido! Então, ao olhar para o desenho, o que vemos? Um farol formado por duas componentes: uma torre e uma luz no cimo da torre. Elas não são completamente independentes pois a posição da torre condiciona a posição da luz. Mas vamos esquecer isso tudo para já e pensar apenas na decomposição.
01.""" Farol."""
02. 
03.import turtle
04. 
05.def farol():
06.    # torre
07.    # luz
08.    pass
09. 
10.if __name__ == '__main__':
11.    pass
É bizarro mas este programa até pode ser executado... embora não faça nada. Vamos agora concentrarmo-nos no desenho da torre. Uma vez mais, quando olhamos o que vemos? Elementos com a forma de quadrados uns em cima dos outros. E que mais? Bom, o tamanho do quadrado diminui à medida que se sobe e as cores vão alternando entre o vermelho e o branco. Significa isto que se tivermos código que nos permita desenhar um quadrado, na posição da tartaruga, com um dado determinado e uma certa cor podemos ter a nova vida facilitada. Vamos então a isso e fixemos a cor vermelha.
01.""" Desenha um quadrado vermelho de um certo tamanho numa dada posição."""
02.# Prepara
03.turtle.fillcolor(‘red’)
04.turtle.begin_fill()
05.# Desenha quadrado
06.for i in range(4):
07.    turtle.forward(tamanho)
08.    turtle.left(90)
09.turtle.end_fill()
Este código funciona um pouco como um bloco construtor. Vamos usá-lo então para desenhar a torre. Não nos custa perceber que vamos repetir um certo número de vezes o desenho do quadrado. Admitamos para já que nos esquecemos das imposições do problema e nos concentramos apenas na altura.
01.import turtle
02. 
03.def farol(altura,lado):
04.    # torre
05.    for i in range(altura):
06.        # Desenha quadrado vermelho
07.        turtle.fillcolor('red')
08.        turtle.begin_fill()
09.        for i in range(4):
10.            turtle.forward(lado)
11.            turtle.left(90)
12.        turtle.end_fill()
13.    # luz
14.    pass
15. 
16.if __name__ == '__main__':
17.    farol(3,50)
18.    turtle.exitonclick()
Como se vê começam a aparecer os parâmetros formais (altura, tamanho do lado). Usando o bloco construtor embebido num ciclo for mandamos desenhar a torre. Mas se corrermos o código o resultado é decepcionante. Em vez de uma torre um único quadrado. Porquê? É óbvio que nos esquecemos de dizer que a posição dos sucessivos quadrado vai mudando. Como? Bom em relação ao eixo dos xx (horizontal) é igual, mudando apenas a coordenada dos yy por um valor igual ao lado. Lembre-se: estamos para já a esquecer que o lado varia em comprimento em função da altura!!! Alteremos então o código.
01.import turtle
02. 
03.def farol(altura,lado, posx,posy):
04.    # torre
05.    vai_para(posx, posy)
06.    for i in range(altura):
07.        # quadrado colorido
08.        turtle.fillcolor('red')
09.        turtle.begin_fill()
10.        for i in range(4):
11.            turtle.forward(lado)
12.            turtle.left(90)
13.        turtle.end_fill()
14.        vai_para(turtle.xcor(), turtle.ycor() + lado)
15.    # luz
16.    pass
17. 
18.def vai_para(posx,posy):
19.    """ Vai para (posx, posy) sem deixar rasto."""
20.    turtle.penup()
21.    turtle.goto(posx,posy)
22.    turtle.pendown()
23. 
24.if __name__ == '__main__':
25.    farol(5,50,0,0)
26.    turtle.exitonclick()
27.        
E o novo resultado: Notar que introduzimos um sub-programa (vai_para) para colocar a tartaruga na posição desejada sem deixar rasto enquanto se movimenta. O que pode fazer uma simples alteração do código!!! Só é pena é ser tudo da mesma cor... Então é tempo de pensar um pouco nisso. Numeremos os quadrados como fazemos com as sequências: o da base é o 0, o seguinte o 1 e por aí adiante. Se queremos as cores alternadas então fixemos o vermelho para as posições pares e o branco para as ímpares. Modifiquemos o código por forma a testar se a posição é par ou é ímpar.
01.def farol(altura,lado, posx,posy):
02.    # torre
03.    vai_para(posx, posy)
04.    for i in range(altura):
05.        # quadrado colorido
06.        if i % 2 == 0: # <-- par?
07.            turtle.fillcolor('red')
08.        else:
09.            turtle.fillcolor('white')
10.        turtle.begin_fill()
11.        for i in range(4):
12.            turtle.forward(lado)
13.            turtle.left(90)
14.        turtle.end_fill()
15.        vai_para(turtle.xcor(), turtle.ycor() + lado)
16.    # luz
17.    pass
Façamos o boneco de novo.
Fantástico, não é? Agora ... faça-se luz. Vamos escrever código para desenhar um semi-círculo amarelo com um dado raio, no topo da torre e acrescentar ao programa.
01.def farol(altura,lado, posx,posy):
02.    # torre
03.    vai_para(posx, posy)
04.    for i in range(altura):
05.        # quadrado colorido
06.        if i % 2 == 0:
07.            turtle.fillcolor('red')
08.        else:
09.            turtle.fillcolor('white')
10.        turtle.begin_fill()
11.        for i in range(4):
12.            turtle.forward(lado)
13.            turtle.left(90)
14.        turtle.end_fill()
15.        vai_para(turtle.xcor(), turtle.ycor() + lado)
16.    # luz
17.    turtle.forward(lado)
18.    turtle.setheading(90) # <--- Atenção!
19.    turtle.fillcolor('yellow')
20.    turtle.begin_fill()
21.    turtle.circle(lado/2,180)
22.    turtle.end_fill()
Toca a executar.
Notar como foi resolvido o problema da orientação do semi-círculo. Quase pronto. Falta o detalhe dos quadrados a decrescer de um certo valor, a que chamaremos decremento. Pensemos um pouco. Se cada vez que subimos um degrau o lado decresce de um valor (decremento) e os quadrados têm que estar centrados, onde devemos colocar a tartaruga a cada etapa? E como mudamos o valor do lado? Em que zona do código fazemos as alterações? Não é difícil perceber que só temos que fazer avançar o valor da coordenada xx e diminuir o tamanho do lado. Eis a nova solução.
01.def farol(altura,lado, posx,posy, decremento):
02.    # torre
03.    vai_para(posx, posy)
04.    for i in range(altura):
05.        # quadrado colorido
06.        if i % 2 == 0:
07.            turtle.fillcolor('red')
08.        else:
09.            turtle.fillcolor('white')
10.        turtle.begin_fill()
11.        for i in range(4):
12.            turtle.forward(lado)
13.            turtle.left(90)
14.        turtle.end_fill()
15.        vai_para(turtle.xcor() + decremento/2, turtle.ycor() + lado) # <-- Eureka!
16.        lado = lado - decremento # <--- Eureka (2)
17.         
18.    # luz
19.    turtle.forward(lado)
20.    turtle.setheading(90)
21.    turtle.fillcolor('yellow')
22.    turtle.begin_fill()
23.    turtle.circle(lado/2,180)
24.    turtle.end_fill()
25.    turtle.hideturtle()
E já está!!! Fácil, não é?? Notar o aparecimento do novo parâmetro decremento e a instrução para esconder a tartaruga no final. O boneco é igual ao primeiro que mostrámos...
Vamos à moral da história. Um problema relativamente difícil pode tornar-se mais simples dividindo-o em sub-problemas mais simples e abordando as especificações em sequência. Muitas vezes (sempre?) quando acabamos de resolver um problema olhamos para o código e pensamos: posso melhorar o código (elegância, legibilidade,...), posso usá-lo noutros problemas? Uma modificação simples que podemos fazer é alterar o código para que as cores não sejam fixas. Uma espécie de farol UCB.
01.import turtle
02. 
03.def farol(altura,lado, posx,posy, decremento, cor_1,cor_2,cor_3):
04.    # torre
05.    vai_para(posx, posy)
06.    for i in range(altura):
07.        # quadrado colorido
08.        if i % 2 == 0:
09.            turtle.fillcolor(cor_1)
10.        else:
11.            turtle.fillcolor(cor_2)
12.        turtle.begin_fill()
13.        for i in range(4):
14.            turtle.forward(lado)
15.            turtle.left(90)
16.        turtle.end_fill()
17.        vai_para(turtle.xcor() + decremento/2, turtle.ycor() + lado)
18.        lado = lado - decremento
19.         
20.    # luz
21.    turtle.forward(lado)
22.    turtle.setheading(90)
23.    turtle.fillcolor(cor_3)
24.    turtle.begin_fill()
25.    turtle.circle(lado/2,180)
26.    turtle.end_fill()
27.    turtle.hideturtle()
28. 
29.def vai_para(posx,posy):
30.    """ Vai para (posx, posy) sem deixar rasto."""
31.    turtle.penup()
32.    turtle.goto(posx,posy)
33.    turtle.pendown()
34. 
35.if __name__ == '__main__':
36.    farol(5,100,0,0,10,'blue', 'green', 'orange')
37.    turtle.exitonclick()
Ei-lo:
That’s all folks!!

Sem comentários:

Enviar um comentário