Este blogue dá suporte à disciplina de IPRP e, naturalmente, preocupa-se com discutir questões de programação, a um nível introdutório, e com métodos de resolução de problemas. Este último aspecto tem contornos gerais (já discutidos num post anterior de Outubro de 2009, intitulado “Resolução de Problemas”), mas devemos procurar saber como é que os princípios genéricos de resolução de problemas se pode aplicar à programação. Neste post vou discorrer um pouco sobre como programar por analogia. Para tal vou socorrer-me do problema da pirâmide de números já abordado, cujo programa aqui retomo.
def piramide(n):
for i in range(1, n+1):
# posiciona
print(4*(n-i)* ' ', end= ' ')
# desenha descendente
for j in range(i,0,-1):
print('%3d' % j , end = ' ')
# desenha ascendente
for j in range(2,i+1):
print('%3d' % j, end=' ')
# muda de linha
print()
Quando executado para
n igual a 4 obtemos uma bela pirâmide:
Vamos supor agora que me pedem um outro tipo de pirâmide:
A pergunta a que temos que responder, uma vez recordados de já ter visto um problema parecido é: qual a diferença? Olhando para as suas imagens torna-se óbvio que não há diferença na forma mas apenas no conteúdo: os números são diferentes e para além disso temos uma sequência ascendente primeiro e depois uma descendente. Vejamos esta última questão primeiro. Invertemos a ordem dos dois ciclos interiores e adaptamos o range à nova situação.
def piramide_2(n):
for i in range(1, n+1):
# posiciona
print(4*(n-i)* ' ', end= ' ')
# desenha ascendente
for j in range(1,i+1):
print('%3d' % j, end=' ')
# desenha descendente
for j in range(i-1,0,-1):
print('%3d' % j , end = ' ')
# muda de linha
print()
Correndo o programa, de novo para n igual a 4, obtemos:
Já não está muito mal. Falta apenas a questão dos números. Se notarmos que a sequência envolve potências de 2, chegamos facilmente à solução por alteração do que é impresso.
def piramide_2(n):
for i in range(1, n+1):
# posiciona
print(4*(n-i)* ' ', end= ' ')
# desenha ascendente
for j in range(1,i+1):
print('%3d' % 2**(j-1), end=' ')
# desenha descendente
for j in range(i-1,0,-1):
print('%3d' % 2**(j-1) , end = ' ')
# muda de linha
print()
Não foi difícil passar de uma solução a outra pois o domínio era o mesmo. Mas admitamos que nos pedem para desenhar outro tipo de pirâmide: uma pirâmide de quadrados como a da figura (mas podendo ter qualquer altura,....):
Uma vez mais a pergunta: que diferença para os casos anteriores? Também aqui a forma é a mesma, alterando-se apenas o conteúdo. Agora cada linha é formada por uma sequência de quadrados idênticos. Não há valores que sobem e descem de acordo com uma regra, não, é tudo igual. Daqui resulta a mesma estrutura de solução, formada por um ciclo for e em que no seu interior temos que desenhar uma linha seguido de uma mudança de linha. O desenho da linha tem dois aspectos: ir para a posição inicial e desenhar a linha. Neste caso há apenas um ciclo for interior.
def pir_quadrados(n,posx, posy,lado):
for i in range(1, n+1):
# desenha linha i
# -- (1) posiciona
# -- (2) desenha
for j in range(1,i+1):
pass
# muda de linha
turtle.hideturtle()
Este esqueleto de solução já tem alguns elementos que derivam da opção por turtle para desenhar os quadrados e também que os parâmetros formais da definição envolvem para além do número de linhas também a posição de referência para o desenho da pirâmide e o tamanho do lado do quadrado. Deixemos de lado por agora na questão da posição e da mudança de linha e fixemo-nos na questão do desenho. Na linha i temos que desenhar i quadrados. Não precisamos de muito tempo para perceber que depois de desenhar um quadrado temos que avançar uma distância igual ao comprimento do lado para desenhar o quadrado seguinte. Logo.
def pir_quadrados(n,posx, posy,lado):
for i in range(1, n+1):
# desenha linha i
# posiciona
# desenha
for j in range(1,i+1):
quadrado(turtle.xcor(),turtle.ycor(), lado)
turtle.setx(turtle.xcor()+lado)
# muda de linha
turtle.hideturtle()
O código depende de uma definição auxiliar para desenhar cada quadrado individualmente. É essa definição que usada o número de vezes ditado pela ordem da linha. É um código que já encontrámos por diversas vezes no passado:
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()
E como mudamos de linha??? Bem, temos que nos posicionar “à esquerda”, na posição de referência para o eixo dos xx e baixar o valor no eixo dos yy de uma quantidade igual ao lado.
def pir_quadrados(n,posx, posy,lado):
for i in range(1, n+1):
# desenha linha i
# posiciona
# 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()
Notar que subtraímos no caso do y pois existe a vontade de fazer crescer a pirâmide para baixo. Vamos executar o programa.
Como era de prever não vemos nenhuma pirâmide pois não resolvemos o problema do posicionamento inicial em cada linha. Mas não é muito diferente do caso das pirâmides dos números, só que aqui temos que fazer avançar ao longo do eixo dos xx e do eixo dos yy. Mas quanto? No caso do y não é difícil: para a linha i temos ir para (n-i)*lado - posy. Se olharmos para a figura de novo vemos que numa dada linha o deslocamento ao longo do eixo dos xx em relação à anterior é de metade do lado. Daí o resultado final:
def pir_quadrados(n,posx, posy,lado):
for i in range(1, n+1):
# desenha linha i
# posiciona
turtle.penup()
turtle.goto(posx+(n-i)*lado/2, (n-i)*lado - posy)
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()
Et voilá!
Mas, esperem. Quando eu mudo de linha o valor de y é actualizado, certo? Correcto. então porque é que no posicionamento me estou a preocupar em redefinir esse valor? Faz sentido!
def pir_quadrados_2(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()
Basta passar de um goto para um setx. Uff!
Mas, se eu simplifico o posicionamento porque faço isso nsa mudança de linha... não podia fazer ao contrário, ou seja, manter a actualização de y no posicionamento e simplificar na mudança de linha? Tem lógica e está bem observado.
def pir_quadrados_3(n,posx, posy,lado):
for i in range(1,n+1):
# desenha linha i
# posiciona
turtle.penup()
turtle.goto(posx+(n-i)*lado/2, (n-i)*lado - posy)
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.setx(posx)
turtle.pendown()
turtle.hideturtle()
Então afinal em que ficamos? Bom numa conclusão simples: os dois sub-problemas estão ligados entre si. Isso acontece com muita frequência e nessa altura temos que ver como é que jogam um com o outro.
Bom. Agora sim, é tempo de encerrar este post. Ou talvez não... Lembrei-me agora de um outro problema que ocorre com frequência em jogos e simulações. Desenhar uma bela grelha colorida como a da figura.
Um... estamos a pensar todos o mesmo, não é verdade? Que diferença em relação aos problemas anteriores? Pensemos no óbvio, na pirâmide de quadrados. Não há problema de posicionamento diferenciado por linha e todas as linhas são iguais. Queremos adaptação mais simples???
def grelha(n,posx, posy,lado,cor):
# inicialização
turtle.pencolor(cor)
for i in range(n):
# desenha linha i
for j in range(n):
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()
Bastou incluir o parâmetro formal para a cor, ter os dois ciclos executados o mesmo número de vezes, e não me preocupar com o posicionamento!!!
Elementar!
(Porque não pensar em variantes destes problemas, ou em usar a grelha final para colocar tartarugas a passear sobre a dita??? A imaginação é o limite.)
Sem comentários:
Enviar um comentário