segunda-feira, 23 de outubro de 2017

Teste #1 - TP4

P1

O seguinte programa possui um ou mais erros/omissões. Identifique o(s) erro(s)/omissão(ões)relacionando-o(s) com o conceito de espaço de nomes do python.
1.def  polar_to_cart_x(r, ang):
2.    x = r * math.cos(ang)
3.    return x
4.if  __name__  == “__main__":
5.    print(polar_to_cart_x (10, pi/4))
Não é feita a importação do módulo math e devemos usar math.pi e não pi.

P2

Escreva um programa para contar o número de pontos que estão dentro de uma circunferência de raio r centrada em (0, 0). O programa deverá receber o número de pontos atestar (num) e o raio (r) como parâmetros. As coordenadas de cada ponto deverão ser pedidas interativamente ao utilizador. No final, o programa deverá indicar o número de pontos que estão dentro da circunferência.
01.import math
02. 
03.def dentro(raio, x, y):
04.    len = math.sqrt(x ** 2 + y ** 2)
05.    if len <= raio:
06.        return True
07.    else:
08.        return False
09.     
10. 
11.def conta_dentro_circulo(num, raio):
12.    num_dentro = 0
13.    for i in range(num):
14.        x = eval(input("x: "))
15.        y = eval(input("y: "))
16.         
17.        if dentro(raio, x, y):
18.            num_dentro = num_dentro + 1
19.     
20.    return num_dentro
21.             
22.     
23.if __name__ == "__main__":
24.    print(conta_dentro_circulo(3, 20))
P3

Usando o módulo turtle, escreva um programa que lhe permita desenhar formas do tipo da ilustrada na Figura. Cada forma é composta por um rectângulo com uma circunferência interna, centrados no mesmo ponto.O raio da circunferência dependerá das dimensões do rectângulo, de acordo com o ilustrado na figura. O programa deverá ser parametrizável de maneira a permitir escolher o número de formas, as dimensões mínima e máxima dos lados dos rectângulos, bem como as posições (x e y) dos mesmos. As coordenadas e dimensões do rectângulo envolvente de cada forma deverão ser geradas aleatoriamente.Soluções modulares serão valorizadas.
01.import turtle
02.import random
03. 
04.def rectangulo(xr, yr, lado1, lado2):
05.    #preparar
06.    turtle.penup()
07.    turtle.goto(xr,yr)
08.    turtle.pendown()
09.     
10.    #desenhar
11.    for i in range(4):
12.        if i%2 == 0:
13.            turtle.forward(lado1)
14.        else:
15.            turtle.forward(lado2)
16.        turtle.right(90)
17.         
18.def circulo(xc, yc, raio):
19.    #preparar
20.    turtle.penup()
21.    turtle.goto(xc, yc)
22.    turtle.pendown()
23.     
24.    #desenhar
25.    turtle.circle(raio)
26.     
27. 
28.def forma_rect_circulo(xf, yf, lado1, lado2):
29.    #rectangulo
30.    rectangulo(xf, yf, lado1, lado2)
31. 
32.    #circulo no interior
33.    raio = min(lado1, lado2) / 2
34.    xc = xf + lado1/2
35.    yc = yf - lado2/2 - raio
36.    circulo(xc, yc, raio)
37.     
38.     
39.def desenha_formas(num, xmin, xmax, ymin, ymax, ladomin, ladomax):
40.     
41.    #sortear e desenhar
42.    for i in range(num):
43.        xf = random.randint(xmin, xmax)
44.        yf = random.randint(ymin, ymax)
45.        lado1 = random.randint(ladomin, ladomax)
46.        lado2 = random.randint(ladomin, ladomax)
47.        forma_rect_circulo(xf, yf, lado1, lado2)
48.     
49.     
50.if __name__ == "__main__":
51.    turtle.hideturtle()  
52.    desenha_formas(5, -300, 300, -300, 300, 50, 150)
53.    turtle.exitonclick()

domingo, 22 de outubro de 2017

Exercícios de Programação Descendente (II)

Nas aulas foi colocado o problema de visualizar na forma de um histograma o resultado de uma experiência de lançamento de um dado. O histograma permitia saber quantas vezes saiu cada número. Pretende-se algo como a figura ilustra.

Vamos novamente tentar perceber se podemos dividir o problema em sub-problemas de modo a tornar a nossa missão mais fácil. Perante o anunciado é evidente que temos dois sub-problemas: (1) efectuar a experiência contando o número de vezes que saiu cada número e, (2) usar essa informação para construir o histograma. As condições do enunciado forçam a usar o modulo turtle para a visualização! É clara a existência de uma dependência entre os dois sub-problemas, pelo que antes de começarmos a resolver em separado cada um deles precisamos definir o seu interface. Uma opção que se impõe é que o sub-problema (1) depende do número de lançamentos n para construir um tuplo (n1,n2,n3,n4,n5,n6), com ni igual ao número de vezes que saiu o número i, e n1+n2+n3+n4+n5+n6 = n. A escolha de um tuplo é natural pois precisamos de um contentor, com a função de memória. Tomadas estas decisões, podemos passar a concretização do programa.
01.import turtle
02. 
03.def dados_histo(n):
04.    # experiência de lançamentos
05.    res = lanca_dado(n)
06.    # visualizaçao
07.    histograma(res)
08.     
09.     
10.def lanca_dado(n):
11.    pass
12. 
13. 
14.def histograma(res):
15.    pass
16. 
17. 
18.if __name__ == '__main__':
19.    n = 100
20.    dados_histo(n)
Vamos começar por resolver o segundo sub-problema. Para tal, vamos de novo decompor o sub-problema em sub-problemas. Aqui temos, pelo menos, duas opções: (a) numa leitura “vertical”, temos quatro sub-problemas: escreve os números de 1 a 6, desenha um traço, desenha os rectângulos e escreve os números correspondentes aos números de vezes que saiu cada número; (b) numa leitura “horizontal, temos seis sub-problemas idênticos: escrever um número, desenhar um traço, desenhar uma coluna e, novamente, escrever um número. A nossa escolha vai ser a segunda, pois é aquela que nos permite ter mais graus de liberdade.
01.import turtle
02. 
03.def dados_histo(n):
04.    # experiência de lançamentos
05.    res = lanca_dado(n)
06.    # visualizaçao
07.    histograma(res)
08.     
09.     
10.def lanca_dado(n):
11.    pass
12. 
13.def histograma(res):
14.    posx = -100
15.    posy = 0
16.    comp_linha = 80
17.    for i,alt in enumerate(res):
18.        # desenha caso i
19.        desenha(i+1,alt,posx,posy,comp_linha)
20.        # define parâmetros
21.        posx = posx + comp_linha
22. 
23.def desenha(i,alt, posx,posy,comp_linha):
24.    pass
25. 
26.if __name__ == '__main__':
27.    n = 100
28.    teste = (46,39,105,0,44,5)
29.    histograma(teste)
30.    #dados_histo(n)
No esboço de solução apresentado podemos verificar que o programa histograma se limita a desenhar cada caso em sequência. Note como conseguimos os valores do número e do número de vezes que saiu graça ao uso de enumerate. Note ainda que a opção tomada obriga a que cada caso singular tenha que saber as quatro componentes relevantes: posição, tamanho do traço, numero do dado e numero de vezes que saiu. A posição para desenhar a coluna vai ser o centro pelo que pode ser calculada a partir do conhecimento do tamanho do traço. Tal como está, podemos testar o programa … mesmo que este não faça nada! Esta é uma das vantagens da programação descendente: podemos testar primeiro as soluções para os problemas e depois de as integrarmos no programa principal, testar o programa principal. A eliminação de eventuais erros é deste modo mais fácil de fazer. Passemos ao caso mais básico. São quatro so sub-problemas básicos que o compõem: número, linha, coluna, número. No entanto os dois problema de escrita de um número são na realidade o mesmo.
1.def desenha(i,alt, posx,posy,comp_linha):
2.    # escreve número i  
3.    # desenha linha
4.    # desenha coluna
5.    # escreve número de vezes (alt) que saiu o número i 
6.    pass
Podemos resolver cada um destes três sub-problemas e testá-los isoladamente.
01.def coluna(posx,posy, lado_1, lado_2,cor):
02.    # posiciona
03.    turtle.penup()
04.    turtle.goto(posx,posy)
05.    turtle.pendown()
06.    # desenha
07.    turtle.color(cor)
08.    turtle.begin_fill()
09.    for i in range(2):
10.        turtle.forward(lado_1)
11.        turtle.left(90)
12.        turtle.forward(lado_2)
13.        turtle.left(90)
14.    turtle.end_fill()
15.    turtle.hideturtle()
16.     
17.def linha(posx,posy,comp_linha,cor):
18.    # posiciona
19.    turtle.penup()
20.    turtle.goto(posx,posy)
21.    turtle.color(cor)
22.    turtle.pendown()
23.    # linha
24.    turtle.forward(comp_linha)
25.    turtle.hideturtle()
26.     
27.def escreve_numero(posx,posy,fonte,valor,cor):
28.    # posiciona
29.    turtle.penup()
30.    turtle.goto(posx,posy)
31.    turtle.color(cor) 
32.    turtle.pendown()
33.    # escreve
34.    turtle.write(valor,font=fonte)
35.    turtle.hideturtle()
Agora precisamos de integrar esses sub-problemas no sub-problema de desenho de uma componente. Aqui vamos ter que perceber as relações entre cada um dos sub-componentes. Em primeiro lugar, decidimos que a largura da coluna será igual a metade do comprimento do traço. Em segundo lugar, fixamos a fonte no tamanho 12 e controlamos a posição da escrita.
01.def desenha(i,alt, posx,posy,comp_linha):
02.    # escreve número i
03.    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')   
04.    # desenha linha
05.    linha(posx,posy,comp_linha,'red')
06.    # desenha coluna
07.    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
08.    # escreve número de vezes (alt) que saiu o número i 
09.    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
Podemos agora testar o problema de visualizar. E está na hora de ver tudo junto.
01.import turtle
02. 
03.def dados_histo(n):
04.    # experiência de lançamentos
05.    res = lanca_dado(n)
06.    # visualizaçao
07.    histograma(res)
08.     
09.     
10.def lanca_dado(n):
11.    pass
12. 
13. 
14.def histograma(res):
15.    posx = -100
16.    posy = 0
17.    comp_linha = 80
18.    for i,alt in enumerate(res):
19.        # desenha caso i
20.        desenha(i+1,alt,posx,posy,comp_linha)
21.        # define parâmetros
22.        posx = posx + comp_linha
23. 
24.def desenha(i,alt, posx,posy,comp_linha):
25.    # escreve número i
26.    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')   
27.    # desenha linha
28.    linha(posx,posy,comp_linha,'red')
29.    # desenha coluna
30.    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
31.    # escreve número de vezes (alt) que saiu o número i 
32.    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
33. 
34. 
35.def coluna(posx,posy, lado_1, lado_2,cor):
36.    # posiciona
37.    turtle.penup()
38.    turtle.goto(posx,posy)
39.    turtle.pendown()
40.    # desenha
41.    turtle.color(cor)
42.    turtle.begin_fill()
43.    for i in range(2):
44.        turtle.forward(lado_1)
45.        turtle.left(90)
46.        turtle.forward(lado_2)
47.        turtle.left(90)
48.    turtle.end_fill()
49.    turtle.hideturtle()
50.     
51.def linha(posx,posy,comp_linha,cor):
52.    # posiciona
53.    turtle.penup()
54.    turtle.goto(posx,posy)
55.    turtle.color(cor)
56.    turtle.pendown()
57.    # linha
58.    turtle.forward(comp_linha)
59.    turtle.hideturtle()
60.     
61.def escreve_numero(posx,posy,fonte,valor,cor):
62.    # posiciona
63.    turtle.penup()
64.    turtle.goto(posx,posy)
65.    turtle.color(cor) 
66.    turtle.pendown()
67.    # escreve
68.    turtle.write(valor,font=fonte)
69.    turtle.hideturtle()
70.   
71.     
72.     
73.     
74.if __name__ == '__main__':
75.    n = 100
76.    teste = (46,39,105,0,44,5)
77.    fonte = ('Arial', 24, 'bold')
78.    posx = 0
79.    posy = 0
80.    cor_1 = 'black'
81.    cor_2 = 'red'
82.    comp = 80
83.    lado_1 = comp/2
84.    lado_2 = 50
85.    #escreve_numero(posx,posy,fonte,n,cor)
86.    #linha(posx,posy,comp,cor_2)
87.    #coluna(posx,posy, lado_1, lado_2,cor_2)
88.    #desenha(4,100, posx,posy,comp)
89.    histograma(teste)
90.    #dados_histo(n)
91.    turtle.exitonclick()
Agora é a vez do primeiro sub-problema, simular o lançamento do dado. Já fizemos isso em problemas anteriores semelhantes. Aqui a novidade reside no facto de querermos memorizar os resultados. Dado o facto de estarmos a usar tuplos, que são objectos imutáveis, vamos decompor esta questão em duas: (1) guardar os valores saídos em cada lançamento, (2) contar quantas vezes saiu cada um. Solução óbvia:
01.def lanca_dado(n):
02.    # lançamento
03.    resultado = tuple()
04.    for i in range(n):
05.        numero = random.randint(1,6)
06.        resultado = resultado + (numero,)
07.    # contagem
08.    conta = tuple()
09.    for i in range(1,7):
10.        conta_i = resultado.count(i)
11.        conta = conta + (conta_i,)
12.    return conta
Podemos finalmente testar o programa completo.
001.import turtle
002.import random
003. 
004.def dados_histo(n):
005.    # experiência de lançamentos
006.    res = lanca_dado(n)
007.    # visualizaçao
008.    histograma(res)
009.     
010.     
011.def lanca_dado(n):
012.    # lançamento
013.    resultado = tuple()
014.    for i in range(n):
015.        numero = random.randint(1,6)
016.        resultado = resultado + (numero,)
017.    # contagem
018.    conta = tuple()
019.    for i in range(1,7):
020.        conta_i = resultado.count(i)
021.        conta = conta + (conta_i,)
022.    return conta
023. 
024. 
025.def histograma(res):
026.    posx = -100
027.    posy = 0
028.    comp_linha = 80
029.    for i,alt in enumerate(res):
030.        # desenha caso i
031.        desenha(i+1,alt,posx,posy,comp_linha)
032.        # define parâmetros
033.        posx = posx + comp_linha
034. 
035.def desenha(i,alt, posx,posy,comp_linha):
036.    # escreve número i
037.    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')   
038.    # desenha linha
039.    linha(posx,posy,comp_linha,'red')
040.    # desenha coluna
041.    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
042.    # escreve número de vezes (alt) que saiu o número i 
043.    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
044. 
045. 
046.def coluna(posx,posy, lado_1, lado_2,cor):
047.    # posiciona
048.    turtle.penup()
049.    turtle.goto(posx,posy)
050.    turtle.pendown()
051.    # desenha
052.    turtle.color(cor)
053.    turtle.begin_fill()
054.    for i in range(2):
055.        turtle.forward(lado_1)
056.        turtle.left(90)
057.        turtle.forward(lado_2)
058.        turtle.left(90)
059.    turtle.end_fill()
060.    turtle.hideturtle()
061.     
062.def linha(posx,posy,comp_linha,cor):
063.    # posiciona
064.    turtle.penup()
065.    turtle.goto(posx,posy)
066.    turtle.color(cor)
067.    turtle.pendown()
068.    # linha
069.    turtle.forward(comp_linha)
070.    turtle.hideturtle()
071.     
072.def escreve_numero(posx,posy,fonte,valor,cor):
073.    # posiciona
074.    turtle.penup()
075.    turtle.goto(posx,posy)
076.    turtle.color(cor) 
077.    turtle.pendown()
078.    # escreve
079.    turtle.write(valor,font=fonte)
080.    turtle.hideturtle()
081.   
082.     
083.     
084.     
085.if __name__ == '__main__':
086.    n = 100
087.    teste = (46,39,105,0,44,5)
088.    fonte = ('Arial', 24, 'bold')
089.    posx = 0
090.    posy = 0
091.    cor_1 = 'black'
092.    cor_2 = 'red'
093.    comp = 80
094.    lado_1 = comp/2
095.    lado_2 = 50
096.    #escreve_numero(posx,posy,fonte,n,cor)
097.    #linha(posx,posy,comp,cor_2)
098.    #coluna(posx,posy, lado_1, lado_2,cor_2)
099.    #desenha(4,100, posx,posy,comp)
100.    #histograma(teste)
101.    #print(lanca_dado(n))
102.    dados_histo(n)
103.    turtle.exitonclick()
104.     
105.    
Para concluir o exercício, experimente com diferentes valores de tentativas. Que conclusões pode tirar à medida que n aumenta?? Identifique os pontos em que a solução não é genérica. como pode alterar a situação??

Na sala, alguns disseram que nos histogramas as colunas não estão separadas. A adaptação do código feito para que a visualização seja essa é mínima: retirar a linha na definição desenha, separar a cor do traço (pencolor) da cor de preenchimento (fillcolor) em desenha para que as colunas fiquem claramente a ver-se e, na função histograma, alterar o posicionamento ao longo do eixo dos xx da cada coluna.
001.import turtle
002.import random
003. 
004.def dados_histo(n):
005.    # experiência de lançamentos
006.    res = lanca_dado(n)
007.    # visualizaçao
008.    histograma(res)
009.     
010.     
011.def lanca_dado(n):
012.    # lançamento
013.    resultado = tuple()
014.    for i in range(n):
015.        numero = random.randint(1,6)
016.        resultado = resultado + (numero,)
017.    # contagem
018.    conta = tuple()
019.    for i in range(1,7):
020.        conta_i = resultado.count(i)
021.        conta = conta + (conta_i,)
022.    return conta
023. 
024. 
025.def histograma(res):
026.    posx = -100
027.    posy = 0
028.    comp_linha = 80
029.    for i,alt in enumerate(res):
030.        # desenha caso i
031.        desenha(i+1,alt,posx,posy,comp_linha)
032.        # define parâmetros
033.        posx = posx + comp_linha/2
034. 
035.def desenha(i,alt, posx,posy,comp_linha):
036.    # escreve número i
037.    escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')   
038.    # desenha coluna
039.    coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
040.    # escreve número de vezes (alt) que saiu o número i 
041.    escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
042. 
043. 
044.def coluna(posx,posy, lado_1, lado_2,cor):
045.    # posiciona
046.    turtle.penup()
047.    turtle.goto(posx,posy)
048.    turtle.pendown()
049.    # desenha
050.    turtle.pencolor('black')
051.    turtle.fillcolor(cor)
052.    turtle.begin_fill()
053.    for i in range(2):
054.        turtle.forward(lado_1)
055.        turtle.left(90)
056.        turtle.forward(lado_2)
057.        turtle.left(90)
058.    turtle.end_fill()
059.    turtle.hideturtle()
060.     
061.def linha(posx,posy,comp_linha,cor):
062.    # posiciona
063.    turtle.penup()
064.    turtle.goto(posx,posy)
065.    turtle.color(cor)
066.    turtle.pendown()
067.    # linha
068.    turtle.forward(comp_linha)
069.    turtle.hideturtle()
070.     
071.def escreve_numero(posx,posy,fonte,valor,cor):
072.    # posiciona
073.    turtle.penup()
074.    turtle.goto(posx,posy)
075.    turtle.color(cor) 
076.    turtle.pendown()
077.    # escreve
078.    turtle.write(valor,font=fonte)
079.    turtle.hideturtle()
080.   
081.     
082.     
083.     
084.if __name__ == '__main__':
085.    n = 1000
086.    teste = (46,39,105,0,44,5)
087.    fonte = ('Arial', 24, 'bold')
088.    posx = 0
089.    posy = 0
090.    cor_1 = 'black'
091.    cor_2 = 'red'
092.    comp = 80
093.    lado_1 = comp/2
094.    lado_2 = 50
095.    #escreve_numero(posx,posy,fonte,n,cor)
096.    #linha(posx,posy,comp,cor_2)
097.    #coluna(posx,posy, lado_1, lado_2,cor_2)
098.    #desenha(4,100, posx,posy,comp)
099.    #histograma(teste)
100.    #print(lanca_dado(n))
101.    dados_histo(n)
102.    turtle.exitonclick()
103.    
Para o leitor: e se quisermos que o histograma possa ter uma orientação qualquer??

sábado, 21 de outubro de 2017

Verdades...

Pense nisto ...

Exercícios de Programação Descendente (I)

Durante as aulas foi colocado o problema de desenvolver uma solução para a criação da imagem visual do símbolo da radioactividade.
Perante este problema a reacção primeira deve ser a de equacionar a possibilidade de decompor o problema em sub-problemas, se possível, independentes. Caso não sejam independentes temos que acordar primeiro o modo como se relacionam.

Não creio ser difícil identificar três sub-problemas: desenhar um quadrado, desenhar três sectores e desenhar uma circunferência. A dependência neste caso é a posição relativa de cada uma das três componentes. Com base nesta abordagem podemos escrever um primeiro esboço de solução.
01.import turtle
02. 
03.def radioactividade():
04.    # desenha quadrado
05.    quadrado()
06.    # desenha sectores
07.    sectores()
08.    #desenha circunferência
09.    circunferencia()
10.     
11.     
12.def quadrado():
13.    pass
14. 
15.def sectores():
16.    pass
17. 
18.def circunferencia():
19.    pass
20. 
21.if __name__ == '__main__':
22.    radioactividade()
Esta solução já pode ser testada embora não faça nada! Note-se que as definições ainda não têm argumentos… Vamos tomar nova decisão: dar o máximo liberdade à resolução de cada um dos sub-problemas. Por exemplo, no caso do quadrado, vamos criar uma definição que permita desenhar um quadrado parametrizado pelo tamanho do lado, a posição, a orientação e a cor. Já sabemos como fazer isso.
01.import turtle
02. 
03.def radioactividade(lado,posx,posy,orientacao,cor):
04.    # desenha quadrado
05.    quadrado(lado,posx,posy,orientacao,cor)
06.    # desenha sectores
07.    sectores()
08.    #desenha circunferência
09.    circunferencia()
10.     
11.     
12. 
13.def quadrado(lado,posx,posy,orientacao,cor):
14.    turtle.up()
15.    turtle.goto(posx,posy)
16.    turtle.setheading(orientacao)
17.    turtle.pendown()
18.    turtle.fillcolor(cor)
19.    turtle.begin_fill()
20.    for i in range(4):
21.        turtle.forward(lado)
22.        turtle.left(90)
23.    turtle.end_fill()
24.    turtle.hideturtle()
25. 
26.def sectores():
27.    pass
28. 
29.def circunferencia():
30.    pass
31. 
32.if __name__ == '__main__':
33.    lado = 100
34.    posx = -50
35.    posy = -50
36.    orientacao = 30
37.    cor = 'yellow'
38.    radioactividade(lado,posx,posy,orientacao,cor)
Podemos testar isoladamente a definição quadrado e/ou testá-la no contexto da definição do símbolo da radioactividade. O normal, em programas grandes é que o teste seja feito primeiro isoladamente e só depois no interior do programa principal. Trata-se de mais uma vantagem deste tipo de programação, apelidada de programação descendente, que conduz a soluções modulares. Para além de os testes serem mais fáceis de fazer, também ficamos com código reutilizável!

Resolvida esta questão vamos escolher um dos dois sub-problemas restantes. Optamos pela circunferência. Também aqui já sabemos o que fazer.
01.def circunferencia(raio,posx,posy,orienta,cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.setheading(orienta)
05.    turtle.pendown()
06.    turtle.fillcolor(cor)
07.    turtle.begin_fill()
08.    turtle.circle(raio)
09.    turtle.end_fill()
10.    turtle.hideturtle()
Se testarmos esta definição verificamos que está tudo funcional como pretendido. Naturalmente queremos testar esta solução no interior do nosso programa principal, e aqui, somos confrontados com o facto dos tamanhos (lado e raio), das posições e das orientações estarem relacionadas. Com um pouco de análise não será difícil chegar a uma solução aceitável.
01.import turtle
02. 
03.def radioactividade(lado,posx,posy,orientacao,cor, cor_c):
04.    # desenha quadrado
05.    quadrado(lado,posx,posy,orientacao,cor)
06.    # desenha sectores
07.    sectores()
08.    #desenha circunferência
09.    raio = lado/10
10.    posx_c = posx + lado/2 + raio
11.    posy_c = posy + lado/2
12.    orientacao_c = orientacao + 90
13.    circunferencia(raio,posx_c,posy_c,orientacao_c,cor_c)
14.     
15.     
16. 
17.def quadrado(lado,posx,posy,orientacao,cor):
18.    turtle.up()
19.    turtle.goto(posx,posy)
20.    turtle.setheading(orientacao)
21.    turtle.pendown()
22.    turtle.fillcolor(cor)
23.    turtle.begin_fill()
24.    for i in range(4):
25.        turtle.forward(lado)
26.        turtle.left(90)
27.    turtle.end_fill()
28.    turtle.hideturtle()
29. 
30.def sectores():
31.    pass
32. 
33.def circunferencia(raio,posx,posy,orienta,cor):
34.    turtle.penup()
35.    turtle.goto(posx,posy)
36.    turtle.setheading(orienta)
37.    turtle.pendown()
38.    turtle.fillcolor(cor)
39.    turtle.begin_fill()
40.    turtle.circle(raio)
41.    turtle.end_fill()
42.    turtle.hideturtle()
43. 
44.if __name__ == '__main__':
45.    lado = 100
46.    posx = -50
47.    posy = -50
48.    orientacao = 0
49.    cor = 'yellow'
50.    raio = lado/10
51.    cor_c = 'black'
52.    #circunf(raio,posx,posy,orientacao,cor)
53.    radioactividade(lado,posx,posy,orientacao,cor, cor_c)
54.    turtle.exitonclick()
Como se pode ver o desenho da circunferência é precedido do cálculo dos valores apropriados para o tamanho do raio, a posição e a orientação. Repare que para que o centro da circunferência coincida com o centro do quadrado, a tartaruga tem que ser colocada numa posição em que veja esse centro à sua esquerda e à distância do raio! O leitor atento notará que a orientação inicial é de zero graus, pois é isto que nos é pedido. Caso o valor seja diferente o posicionamento do centro da circunferência também é diferente.

Deixámos para o fim o problema dos sectores. Também aqui é possível decompor este problema em três sub-problemas equivalentes. Vejamos então como podemos desenhar um sector com total liberdade.
01.def sector(raio,posx,posy,orientacao,cor,amplitude):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.setheading(orientacao)
05.    turtle.color(cor)
06.    turtle.pendown()
07.    turtle.begin_fill()
08.    turtle.forward(raio)
09.    turtle.left(90)
10.    turtle.circle(raio,amplitude)
11.    turtle.left(90)
12.    turtle.forward(raio)
13.    turtle.end_fill()
14.    turtle.setheading(orientacao)
15.    turtle.hideturtle()
O desenho tem três partes: desenho do raio, desenho do arco, desenho do raio. Notar que no final queremos ter a tartaruga com a orientação inicial. Porquê?? Uma vez mais, um leitor atento pode ser levado a pensar que o desenho da circunferência e o desenho do sector poderiam ser unidos numa única definição. Afinal, parece que apenas diferem do parâmetro amplitude. Mas será mesmo assim? Quem quiser pode explorar esse caminho e verificar as questões que se colocam.

Como desenhamos os três sectores? Não é difícil perceber que temos que repetir o desenho de um sector alterando apenas a orientação de 120 graus. Será que o facto de a tartaruga que desenha um sector terminar com a mesma orientação que a inicial ajudou???
1.def sectores(raio,posx,posy,orientacao,cor,amplitude):
2.    for i in range(3):
3.        sector(raio,posx,posy,orientacao,cor,amplitude)
4.        orientacao = orientacao + 120
Para terminar o trabalho temos que incorporar esta solução no programa principal.
01.import turtle
02. 
03.def radioactividade(lado,posx,posy,orientacao,cor, cor_c, cor_s):
04.    # desenha quadrado
05.    quadrado(lado,posx,posy,orientacao,cor)
06.    # desenha sectores
07.    raio_s = lado/4
08.    posx_s = posx + lado/2
09.    posy_s = posy + lado/2
10.    orientacao_s = orientacao
11.    amplitude = 60
12.    sectores(raio_s,posx_s,posy_s,orientacao_s,cor_s,amplitude)
13.    #desenha circunferência
14.    raio = lado/10
15.    posx_c = posx + lado/2 + raio
16.    posy_c = posy + lado/2
17.    orientacao_c = orientacao + 90
18.    circunferencia(raio,posx_c,posy_c,orientacao_c,cor_c)
19.     
20.     
21. 
22.def quadrado(lado,posx,posy,orientacao,cor):
23.    turtle.up()
24.    turtle.goto(posx,posy)
25.    turtle.setheading(orientacao)
26.    turtle.pendown()
27.    turtle.fillcolor(cor)
28.    turtle.begin_fill()
29.    for i in range(4):
30.        turtle.forward(lado)
31.        turtle.left(90)
32.    turtle.end_fill()
33.    turtle.hideturtle()
34. 
35.def sectores(raio,posx,posy,orientacao,cor,amplitude):
36.    for i in range(3):
37.        sector(raio,posx,posy,orientacao,cor,amplitude)
38.        orientacao = orientacao + 120
39.         
40. 
41.def sector(raio,posx,posy,orientacao,cor,amplitude):
42.    turtle.penup()
43.    turtle.goto(posx,posy)
44.    turtle.setheading(orientacao)
45.    turtle.color(cor)
46.    turtle.pendown()
47.    turtle.begin_fill()
48.    turtle.forward(raio)
49.    turtle.left(90)
50.    turtle.circle(raio,amplitude)
51.    turtle.left(90)
52.    turtle.forward(raio)
53.    turtle.end_fill()
54.    turtle.setheading(orientacao)
55.    turtle.hideturtle()
56. 
57.def circunferencia(raio,posx,posy,orienta,cor):
58.    turtle.penup()
59.    turtle.goto(posx,posy)
60.    turtle.setheading(orienta)
61.    turtle.pendown()
62.    turtle.fillcolor(cor)
63.    turtle.begin_fill()
64.    turtle.circle(raio)
65.    turtle.end_fill()
66.    turtle.hideturtle()
67. 
68.if __name__ == '__main__':
69.    lado = 100
70.    posx = -50
71.    posy = -50
72.    orientacao = 0
73.    cor = 'yellow'
74.    raio = lado/10
75.    cor_c = 'black'
76.    raio_s = lado/4
77.    cor_s = 'black'
78.    amplitude = 60
79.    #circunferencia(raio,posx,posy,orientacao,cor)
80.    #sector(raio_s,posx,posy,orientacao,cor_s,amplitude)
81.    #sectores(raio_s,posx,posy,orientacao,cor_s,amplitude)
82.    radioactividade(lado,posx,posy,orientacao,cor, cor_c,cor_s)
83.    turtle.exitonclick()
A definição de alguns parâmetros dos sectores em função dos parâmetros do quadrado não deve oferecer dúvidas. Se executarmos o programa verificamos que não aparece a separação entre a circunferência e os sectores. A solução desse problema é trivial e passa por colocar a cor da caneta da tartaruga, quando desenha a circunferência, a branco.

01.def circunferencia(raio,posx,posy,orienta,cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.setheading(orienta)
05.    turtle.pencolor('white')
06.    turtle.pendown()
07.    turtle.fillcolor(cor)
08.    turtle.begin_fill()
09.    turtle.circle(raio)
10.    turtle.end_fill()
11.    turtle.hideturtle()
Como dissemos atrás esta solução global funciona quando a orientação inicial é zero. Se for outra não funciona. Como alterar a nossa solução mexendo o mínimo possível, por forma ao programa funcionar mesmo quando o quadrado tem uma orientação qualquer? A solução passa por recalcular o centro da figura tendo a orientação em linha de conta. Para isso basta usar um pouco do nosso conhecimento de trigonometria.
01.import turtle
02.import math
03. 
04. 
05.def radioactividade(lado,posx,posy,orientacao,cor, cor_c, cor_s):
06.    # desenha quadrado
07.    quadrado(lado,posx,posy,orientacao,cor)
08.    # desenha sectores
09.    raio_s = lado/4
10.    ang_base = (orientacao* math.pi / 180)
11.    ang = ang_base + math.pi/4    
12.    posx_s = posx + lado/math.sqrt(2) * math.cos(ang)
13.    posy_s = posy + lado/math.sqrt(2) * math.sin(ang)
14.    orientacao_s = orientacao
15.    amplitude = 60
16.    sectores(raio_s,posx_s,posy_s,orientacao_s,cor_s,amplitude)
17.    #desenha circunferência
18.    raio = lado/10
19.    posx_c = posx_s +  raio * math.cos(ang_base)
20.    posy_c = posy_s + raio * math.sin(ang_base)
21.    orientacao_c = orientacao + 90
22.    circunferencia(raio,posx_c,posy_c,orientacao_c,cor_c)
23.     
24.     
25. 
26.def quadrado(lado,posx,posy,orientacao,cor):
27.    turtle.up()
28.    turtle.goto(posx,posy)
29.    turtle.setheading(orientacao)
30.    turtle.pendown()
31.    turtle.fillcolor(cor)
32.    turtle.begin_fill()
33.    for i in range(4):
34.        turtle.forward(lado)
35.        turtle.left(90)
36.    turtle.end_fill()
37.    turtle.hideturtle()
38. 
39.def sectores(raio,posx,posy,orientacao,cor,amplitude):
40.    for i in range(3):
41.        sector(raio,posx,posy,orientacao,cor,amplitude)
42.        orientacao = orientacao + 120
43.         
44. 
45.def sector(raio,posx,posy,orientacao,cor,amplitude):
46.    turtle.penup()
47.    turtle.goto(posx,posy)
48.    turtle.setheading(orientacao)
49.    turtle.color(cor)
50.    turtle.pendown()
51.    turtle.begin_fill()
52.    turtle.forward(raio)
53.    turtle.left(90)
54.    turtle.circle(raio,amplitude)
55.    turtle.left(90)
56.    turtle.forward(raio)
57.    turtle.end_fill()
58.    turtle.setheading(orientacao)
59.    turtle.hideturtle()
60. 
61.def circunferencia(raio,posx,posy,orienta,cor):
62.    turtle.penup()
63.    turtle.goto(posx,posy)
64.    turtle.setheading(orienta)
65.    turtle.pencolor('white')
66.    turtle.pendown()
67.    turtle.fillcolor(cor)
68.    turtle.begin_fill()
69.    turtle.circle(raio)
70.    turtle.end_fill()
71.    turtle.hideturtle()
72. 
73.if __name__ == '__main__':
74.    lado = 200
75.    posx = -50
76.    posy = -50
77.    orientacao = 45
78.    cor = 'yellow'
79.    raio = lado/10
80.    cor_c = 'black'
81.    raio_s = lado/4
82.    cor_s = 'black'
83.    amplitude = 60
84.    radioactividade(lado,posx,posy,orientacao,cor, cor_c,cor_s)
85.    turtle.exitonclick()
Como se pode ver apenas mexemos no programa principal e em zonas localizadas do código, as zonas de definem a interacção entre as partes. E pronto. Espero que da próxima vez que programar procure usar este princípio da decomposição de um problema em sub-problemas!

sexta-feira, 13 de outubro de 2017

Teste # 1 - TP2

P1

O que aparece no lugar do ponto de interrogação?
1.>>> x = 'abacadabra'
2.>>> x[1] = 'zeus'
3.Traceback (most recent call last):
4.  Python Shell, prompt 2, line 1
5.builtins.TypeError: 'str' object does not support item assignment
Aparece um erro pois as cadeias de caracteres são imutáveis não podendo o seu valor ser alterado.

P2

Como saber se uma moeda está enviesada? Fazemos vários lançamentos e comparamos com o valor esperado para uma das duas opções. Como nada neste mundo é perfeito aceitamos uma pequena discrepância em relação a esse valor. Para resolver o problema, vamos devagar e por partes. Primeiro uma versão simples que apenas simula e compara com o caçoe médio esperado.
1.def enviesada_a(n):
2.    # lança e conta
3.    conta = 0
4.    for i in range(n):
5.        conta = conta + random.randint(0,1)  
6.    # analisa
7.    return conta != n//2
Esta versão baseia-se num padrão dec programação conhecido por ciclo - acumulador. O nome conta está associado a um objecto cujo valor corresponde ao numero de vezes que já saiu caras (1). O ciclo é repetido o número de vezes pretendido. A comparação final é feita usando a divisão inteira.

Vamos partir desta solução para a solução final pretendida. A moeda estará enviesada se o valor obtido estiver fora de um dado intervalo.
01.def enviesada(n,perc):
02.    """
03.    n = número de lançamentos
04.    perc = percentagem aceitável [0,1]
05.    """
06.    # lança e conta
07.    conta = 0
08.    for i in range(n):
09.        conta = conta + random.randint(0,1)  
10.    # analisa
11.    espera = n/2
12.    inf_ = (1 - perc)* espera
13.    sup_ = (1 + perc) * espera
14.    return (conta < inf_) or (conta > sup_)
Como se observa usamos agora uma divisão de floats.

P3

Queremos desenhar bonecos como o da figura.
Olhando para a figura observamos que precisamos saber desenhar balões coloridos e uma cauda que é composta de repetições de uma sequência de quatro segmentos com orientações alternadas. Uma solução simples vai envolver três passos:
1.def boneco(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
2.    # desenha balão grande
3.    # desenha balão pequeno
4.    # desenha cauda
5.    pass
Tratemos dos balões isoladamente:
01.def bola(raio, posx,posy, orientacao, cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.pendown()
05.    turtle.setheading(orientacao)
06.    turtle.fillcolor(cor)
07.    turtle.begin_fill()
08.    turtle.circle(raio)
09.    turtle.end_fill()
10.    turtle.hideturtle()
Esta solução corresponde ao que já foi feito nas aulas!!! Vamos tratar da parte nova: a cauda. olhando para a figura vemos que é composta a partir de uma sequência de formas mais simples. Estas por sua vez são formadas por três traços. Eis uma solução genérica para a cauda:
01.def cauda(n, tipo, posx, posy,orientacao, comp, cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.pendown()
05.    turtle.setheading(orientacao)
06.    turtle.pencolor(cor)
07.    for i in range(n):
08.        turtle.forward(comp)
09.        turtle.right(tipo * 60)    
10.    turtle.hideturtle()
Dizemos que é genérica porque usamos o parâmetro n, que no caso que nos interessa será igual a 3. Por outro lado, note-se que o parâmetro tipo é usado para determinar a orientação de cada sequência de três segmentos. tipo pode valer 1 ou -1, pois só temos duas orientações a considerar.

Resolvidas as três questões (balão grande, balão pequeno e cauda), vamos juntar tudo. A primeira questão é a de saber como juntamos os dois balões. A ideia é desenhar o maior e depois, a partir da posição final e da orientação, calcular a posição do centro do balão pequeno. Uma hipótese é:
01.def boneco(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
02.    # desenha balão grande
03.    bola(raio_1,  posx,posy, orientacao, cor_1)
04.    # desenha balão pequeno
05.    turtle.penup()
06.    turtle.setheading(orientacao-90)
07.    turtle.forward(2*raio_2)
08.    turtle.setheading(orientacao)
09.    turtle.pendown()
10.    bola(raio_2,  turtle.xcor(),turtle.ycor(), orientacao, cor_2)
11.    # desenha cauda
Esta solução é fácil de entender se nos lembrarmos que a tartaruga desenha uma circunferência tendo o centro à sua esquerda! Claro que podemos fazer de outro modo:

1.def balao(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
2.    bola(raio_1,  posx,posy, orientacao, cor_1)
3.    bola(-raio_2,  turtle.xcor(),turtle.ycor(), orientacao, cor_2)
Percebe a diferença???

Só falta acrescentar a cauda…
01.def balao(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
02.    bola(raio_1,  posx,posy, orientacao, cor_1)
03.    turtle.penup()
04.    turtle.setheading(orientacao-90)
05.    turtle.forward(2*raio_2)
06.    turtle.setheading(orientacao)
07.    turtle.pendown()
08.    bola(raio_2,  turtle.xcor(),turtle.ycor(), orientacao, cor_2)
09.    for i in range(n):
10.        cauda(3,(-1)**i,turtle.xcor(),turtle.ycor(),turtle.heading(),20,cor_3)
11.    turtle.hideturtle()
E pronto! Percebeu o modo como alternamos a orientação da cauda??? Simplesmente fazendo o tipo igual a (-1)** i, o que faz com que o tipo vá ser alternadamente 1 e -1, como pretendido!

Pode usar este programa para criar variantes. Por exemplo:

01.def baloes(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
02.    # balão grande
03.    bola(raio_1,  posx,posy, orientacao, cor_1)
04.    # n balões pequenos à volta do balão grande...
05.    for i in range(n):
06.        turtle.penup()
07.        turtle.circle(raio_1,360/n)
08.        turtle.pendown()       
09.        bola(-raio_2,  turtle.xcor(),turtle.ycor(), turtle.heading(), cor_2)
10.    turtle.hideturtle()

Teste # 1 - TP1

P1

Quando fazemos :
1.>>> X = X + 1
acontece o seguinte. Primeiro o sistema tenta calcular o objecto associado à expressão X + 1. Para tal procura no espaço dos objectos o valor do objecto associado ao nome X. De seguida, incrementa esse valor de uma unidade e associa o novo objecto ao nome X.

P2

Era-nos pedido uma solução para o problema de saber se após o lançamento de um dado n vezes, o número de vezes que saiu um número par é maior do que o valor médio esperado. Podemos resolver este problema pro aproximações, baseando-nos num padrão de programação nosso conhecido: ciclo - acumulador.

1.def par_impar(n):
2.    conta_par = 0 # o acumulador
3.    for i in range(n):
4.        num = lanca_dado()
5.        # actualiza o acumulador
6.    # define resultado
A implementação da simulação do lançamento do dado é trivial:
1.import random
2. 
3.def lanca_dado():
4.    return random.randint(1,6)
Com estes elementos chegamos facilmente à versão final:
01.import random
02. 
03.def lanca_dado():
04.    return random.randint(1,6)
05. 
06.def par_impar(n):
07.    conta_par = 0
08.    for i in range(n):
09.        num = lanca_dado()
10.        if (num == 2) or (num == 4) or (num == 6): # if num in (2,4,6):
11.            conta_par = conta_par + 1
12.    if conta_par > n/2:
13.        return True
14.    else:
15.        return False
Notar que o teste de saída de número par pode ser abreviado para if num in (2,4,6):

P3

Queremos um programa que nos permita desenhar figuras como a abaixo.

Pedem para poder parametrizar muita coisa: posição, orientação, número de laços e de segmentos tos, cor, tamanho do lado dos laços, tamanho dos segmentos, etc.

A solução passa por dividir o problema em sub-problemas e não tentar resolver tudo de uma vez. Olhando para a figura vemos laços e uma cauda. Os laços podem ser construídos como dois triângulos ligados por um vértice, enquanto a cauda é uma sequência de segmentos. Vamos resolver cada um dos sub-problemas.

Comecemos pelos triângulos coloridos, algo que fizemos nas aulas.
01.def tri_cor(posx,posy,orientacao,lado,cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.pendown()
05.    turtle.setheading(orientacao)
06.    turtle.fillcolor(cor)
07.    turtle.begin_fill()
08.    for i in range(3):
09.        turtle.forward(lado)
10.        turtle.left(120)
11.    turtle.end_fill()
12.    turtle.hideturtle()
Como se pode ver, iniciamos o programa definindo os parâmetros e depois desenhos o triângulo. O laço resulta de desenharmos dois triângulos percebendo que a orientação de ambos está desfasada de 180 graus.

1.def laco(posx,posy,orientacao,lado,cor):
2.    tri_cor(posx,posy,orientacao,lado,cor)
3.    tri_cor(posx,posy,orientacao + 180,lado,cor)
4.    turtle.hideturtle()
Passemos à cauda como sequência de segmentos. Desenhar um segmento com uma dada inclinação é trivial.

1.def seg(posx,posy,comp,orientacao,cor):
2.    turtle.penup()
3.    turtle.goto(posx,posy)
4.    turtle.pendown()
5.    turtle.setheading(orientacao)
6.    turtle.pencolor(cor)
7.    turtle.forward(comp)
8.    turtle.hideturtle()
Podemos agora juntar as peças do nosso puzzle. Se pensarmos um pouco, a estratégia mais interessante para o nosso programa final consiste em desenhar um segmento e de seguida desenhar um laço, repetindo estas acções o número apropriado de vezes. Daí a solução:

01.def boneco(n, posx, posy, orientacao,inc,comp,lado,cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.pendown()
05.    for i in range(n):
06.        if i % 2 == 0:
07.            seg(turtle.xcor(),turtle.ycor(),comp,orientacao + inc,cor)
08.            laco(turtle.xcor(),turtle.ycor(),turtle.heading()-90,lado,cor)
09.        else:
10.            seg(turtle.xcor(),turtle.ycor(),comp,orientacao - inc,cor)
11.            laco(turtle.xcor(),turtle.ycor(),turtle.heading()+90,lado,cor)       
12.    if n % 2 == 0:
13.        seg(turtle.xcor(),turtle.ycor(),comp,orientacao + inc,cor)
14.    else:
15.        seg(turtle.xcor(),turtle.ycor(),comp,orientacao - inc,cor)
E pronto! O leitor atento notará que o if final se deve à necessidade de desenhar o último segmento da cauda. Por outro lado, note como controlamos a orientação da cauda, e como relacionamos a orientação dos segmentos e dos laços. Finalmente, a cor dos laços e dos segmentos é a mesma, mas é trivial fazer com que tenham cor diferente!

Depois de feito o programa, torna-se evidente que podemos simplificar o desenho dos segmentos:
01.def boneco(n, posx, posy, orientacao,inc,comp,lado,cor):
02.    turtle.penup()
03.    turtle.goto(posx,posy)
04.    turtle.pendown()
05.    turtle.pencolor(cor)
06.    for i in range(n):
07.        if i % 2 == 0:
08.            turtle.setheading(orientacao + inc)
09.            turtle.forward(comp)
10.            laco(turtle.xcor(),turtle.ycor(),turtle.heading()-90,lado,cor)
11.        else:
12.            turtle.setheading(orientacao - inc)
13.            turtle.forward(comp)
14.            laco(turtle.xcor(),turtle.ycor(),turtle.heading()+90,lado,cor)
15.    if n % 2 == 0:
16.        turtle.setheading(orientacao + inc)
17.        turtle.forward(comp)
18.    else:
19.        turtle.setheading(orientacao - inc)
20.        turtle.forward(comp)
E chega… ou talvez não!

Para terminar, e embora não fosse necessário, um pequeno programa para desenhar apenas uma cauda ondulante:
01.def cauda(n,posx,posy,comp,orienta_1,orienta_2,cor):
02.    # posiciona
03.    turtle.penup()
04.    turtle.goto(posx,posy)
05.    turtle.pendown()
06.    turtle.pencolor(cor)
07.    for i in range(n):
08.        if i % 2 == 0 :
09.            seg(turtle.xcor(),turtle.ycor(),comp, orienta_1,cor)
10.        else:
11.            seg(turtle.xcor(),turtle.ycor(),comp, orienta_2,cor)
Note-se como controlamos a orientação dos segmentos.