tag:blogger.com,1999:blog-78506343102615943582024-03-05T12:38:37.359+00:00Programação com PythonCoisas avulsas para os alunos de IPRPErnesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.comBlogger295125tag:blogger.com,1999:blog-7850634310261594358.post-73696366679771548032018-01-26T19:52:00.001+00:002018-01-26T19:52:51.139+00:00Exame Recurso - Pergunta 2Como se pode usar o turtle para desenhar figuras como:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfJB7zY5iPPLTqb2ivGAiGZyQlgzD2mLMMKD5puZFguuIGU2dH1pmEo2lTs76Q9IeiQBJkBJFjc4mkHwUGQyqcGuMqxKxaH7rzNCZnUbZOl4TEsg9OtQIBspoFRTPCYfLEG-hxm5MFoKxA/s1600/bolacha.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfJB7zY5iPPLTqb2ivGAiGZyQlgzD2mLMMKD5puZFguuIGU2dH1pmEo2lTs76Q9IeiQBJkBJFjc4mkHwUGQyqcGuMqxKxaH7rzNCZnUbZOl4TEsg9OtQIBspoFRTPCYfLEG-hxm5MFoKxA/s320/bolacha.png" width="311" height="320" data-original-width="620" data-original-height="637" /></a></div>
Devia ser possível fazer variar diversos parâmetros, como o número de arcos, o raio, a cor, a posição. Solução é relativamente trivial:
<pre name=‘code’ class=‘brush:python’>
import turtle as tt
def bolachas(n, raio, orienta=0, posx=0,posy=0,cor='red'):
# inicializa
tt.penup()
tt.setheading(orienta)
tt.goto(posx,posy)
tt.color(cor)
tt.begin_fill()
tt.pendown()
ang = 180 - (360/n)
for i in range(n):
tt.circle(raio,180)
tt.right(ang)
tt.end_fill()
tt.hideturtle()
if __name__ == '__main__':
n= 30
raio=25
orienta = 45
posx = 100
posy = 100
cor = 'blue'
bolachas(n, raio, orienta, posx,posy,cor)
tt.exitonclick()
</pre>
Como se pode ver pela solução, a opção foi a de desenhar semi-circunferências. A única questão relevante, mais complexa, era perceber como rodar a tartaruga após o desenho de cada arco. Como se pode no código acima ver tal está relacionado com o número de arcos.
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-39034935326023343312018-01-26T16:43:00.002+00:002018-01-26T16:43:49.499+00:00Exame Recurso - P1No exame de recurso era apresentada a seguinte sessão no interpretador:
<pre name=‘code’ class=‘brush:python’>
>>> def teste(x):
... def mist(y):
... return x**y
... return mist
...
>>> oops = teste(2)
>>> oops(4)
16
</pre>
e perguntava-se para explicitar o que se tinha passado. <br> <br>
As respostas demonstram a grande <b>confusão conceptual</b> que ainda existe. Mas vamos por partes. A sessão pode ser dividida em três partes. Na primeira, é definida uma função (<i>teste</i>) cujo corpo é formado pela definição de outra função (<i>mist</i>). A função <i>teste</i> devolve como resultado a função <i>mist</i>. Na segunda, é feita a atribuição
<pre name=‘code’ class=‘brush:python’>
oops = teste(2)
</pre>
Em Python, todas as atribuições são do tipo:
<pre name=‘code’ class=‘brush:python’>
nome = expressão
</pre>
Assim o que vai acontecer é que é calculado o valor associado à <b>expressão</b><i> teste(2)</i> e esse valor é um novo objecto de nome … <b>nome</b>! No nosso caso, a expressão vai ter como valor um objecto do tipo função que devolve 2**y, que tem <i>oops</i> como um atributo do tipo <b>nome</b>. Na terceira parte, essa nova função é chamada com o argumento igual a 4, pelo que o resultado que se obtém será 2**4 = 16. <br> <br>
A sessão abaixo ilustra o que acabámos de dizer.
<pre name=‘code’ class=‘brush:python’>
>>> id(teste)
4504739912
>>> type(oops)
<class 'function'>
>>> id(oops)
4504740048
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'oops', ‘teste']
</pre>
Como use pode ver, no <b>espaço de nomes</b> apenas são conhecidos <i>oops</i> e <i>teste</i>. Sao ambos nomes associados a <b>funções distintas</b>. Podemos expandir as coisas recorrendo ao <b>módulo inspect</b>.
<pre name=‘code’ class=‘brush:python’>
>>> import inspect
>>> inspect.getargspec(oops)
ArgSpec(args=['y'], varargs=None, keywords=None, defaults=None)
>>> inspect.getargspec(teste)
ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)
>>> inspect.getclosurevars(oops)
ClosureVars(nonlocals={'x': 2}, globals={}, builtins={}, unbound=set())
>>> inspect.getclosurevars(teste)
ClosureVars(nonlocals={}, globals={}, builtins={}, unbound=set())
</pre>
Como notamos, cada uma das funções tem o seu argumento (<b>x</b>, para <i>teste</i>, <b>y</b>, para <i>oops</i>), mas <i>oops</i> tem <b>x</b> como variável <b>não local</b> instanciada a 2!
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-92183907137600622682017-12-15T10:18:00.000+00:002017-12-15T10:18:24.437+00:00Teste # 2 - Especial<b>P2</b><br><br>
Suponha que tem uma palavra e pretende criar uma outra de maneira que os caracteres da palavra inicial nas posições pares passam para as posições ímpares na nova palavra, e os caracteres das posições ímpares da palavra inicial passam para as posições pares da nova palavra. Por exemplo: \\
<pre name=‘code’ class=‘brush:python’>
>>> palavra = 'a1b2c3d'
>>> print(troca(palavra))
1a2b3cd
</pre>
Implemente o respectivo programa.<br><br>
A solução adoptada começa por dividir a palavra nos seus elementos pares e nos ímpares. Depois, num ciclo, constrói-se a nova palavra. Finalmente, verifica-se o caso do tamanho ser ímpar o que o briga a acrescentar o último elemento na posição par, que não foi introduzido no ciclo.
<pre name=‘code’ class=‘brush:python’>
def troca(pal):
"""Os caracteres nas posições pares passam para ímpares e os nas posições ímpares passam para as posiçõesp pares."""
pares = pal[::2]
impares = pal[1::2]
nova_pal = ''
for i in range(len(pal)//2):
nova_pal += impares[i] + pares[i]
if len(pal)%2 != 0:
nova_pal += pares[-1]
return nova_pal
</pre>
<b>P3</b><br><br>
Suponha que tem uma imagens a preto e branco e que pretende <b>limpar uma parte da imagem</b>, ou seja, colocar os pixeis dessa parte todos a branco. A zona a limpar é dada por dois pontos, o canto superior esquerdo e o canto superior direito. Por exemplo:\\
<pre name=‘code’ class=‘brush:python’>
>>> img = [[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]]
>>> sup = (1,2)
>>> inf = (3,4)
>>> print(limpar(img,sup,inf))
[[1, 1, 1, 1, 1], [1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [1, 1, 0, 0, 0]]
</pre>
Implemente o respectivo programa. <br><br>
A solução apresentada percorre todas a matriz de modo usual, por linhas e para cada linha por colunas, limitando as coordenadas ai interior do rectângulo definido pelo canto inferior esquerdo e santo superior direito, muda os piteis para zero (branco). Antes de proceder à alteração, verifica se a operação é possível.
<pre name=‘code’ class=‘brush:python’>
def limpar(imagem, sup_esq,inf_dir):
""" Limpa a imagem entre o canto superior esaquerdo e o canto superior direito."""
# verifica se é possível
num_linhas = len(imagem)
num_colunas = len(imagem[0])
possivel_linhas = (sup_esq[0] <= inf_dir[0] <= num_linhas )
possivel_colunas = (sup_esq[1] <= inf_dir[1] <= num_colunas)
if possivel_linhas and possivel_colunas:
for linha in range(sup_esq[0], inf_dir[0]+1):
for coluna in range(sup_esq[1],inf_dir[1]+1):
imagem[linha][coluna] = 0
return imagem
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-60320898562391798112017-12-15T09:55:00.003+00:002017-12-15T09:55:57.898+00:00Teste 3 - TP2<b>P2 </b><br><br>
Suponha que tem uma árvore genealógica organizada usando um dicionário. A chave é um tuplo com dois elementos, em que cada elemento é um tuplo com o nome do progenitor e respectiva idade. O valor é uma lista com tuplos, em que cada tuplo tem o nome do descendente e respectiva idade. Um exemplo:
<pre name=‘code’ class=‘brush:python’>
ag = {(('ernesto',64),('anabela',44)): [('daniela', 15)], (('vitor',72),('irm',68)): [('ines', 39)], (('carlos',68),('mena',65)): [('ricardo', 42), ('marcos', 37)],(('josé',85),('lurdes',82)): [('ernesto', 64), ('afonso',74),('vitor',72), ('carlos',68),('isabel',66)], (('joaobranco',70),('isabel',66)): [('anaisabel', 35),('joana',29)], (('jbp',77),('jbm',70)): [('joaobranco', 70), ('graça',68)]}
</pre>
Escreva um programa que dada uma árvore genealógica e o nome de uma pessoa, devolva o <b>nome do sobrinho(a) mais velho(a)</b>. Exemplo:
<pre name=‘code’ class=‘brush:python’>
>>> sob_mais_velho(ag,'afonso')
ricardo
</pre>
Os meus sobrinhos são os filhos dos meus irmãos. Daí fazer sentido ter definições auxiliares para determinar os filhos e os irmãos de uma pessoa.
<pre name=‘code’ class=‘brush:python’>
def irmaos(ag, pessoa):
for prog,filhos in ag.items():
for filho in filhos:
if pessoa == filho[0]:
filhos.remove(filho)
return filhos
def meus_filhos(ag, pessoa):
for prog, filhos in ag.items():
if (pessoa == prog[0][0]) or (pessoa == prog[1][0]):
return filhos
return None
</pre>
Resolvida esta questão, o resto é fácil. Começamos por calcular os irmãos da pessoa. Caso existam, calculamos os filhos de cada um dos irmãos. Finalmente, determinamos qual deles é o mais velho.
<pre name=‘code’ class=‘brush:python’>
def sob_mais_velho(ag,pessoa):
irm = irmaos(ag,pessoa)
if irm:
filhos = []
for irmao in irm:
filhos.extend(meus_filhos(ag,irmao[0]))
velho = filhos[0]
for fil in filhos[1:]:
if velho[1] < fil[1]:
velho = fil
return velho[0]
return ()
</pre>
<b>P3</b> <br><br>
Suponha que tem dois ficheiros de texto com exactamente o mesmo número de linhas. Pretende-se que escreva um programa que dados esses dois ficheiros crie um terceiro cujo conteúdo resulta da <b>junção das linhas</b> dos dois primeiros, feita do seguinte modo. Se A e B forem os dois ficheiros de origem e C o ficheiro resultado, então C vai ter na primeira linha o conteúdo da primeira linha do ficheiro A seguido do conteúdo da primeira linha do ficheiro B, na segunda linha o conteúdo da segunda linha do ficheiro A seguido do conteúdo da segunda linha do ficheiro B, e assim sucessivamente.<br><br>
Solução trivial. Lemos os dois ficheiros e guardamos o seu conteúdo como listas de linhas. De seguida criamos uma nova lista juntando as linhas de mesma ordem. Finalmente, criamos o novo ficheiro. Notar que temos que retirar o indicador de mudança de linha das linhas do primeiro ficheiro. Usamos o tab como separador.
<pre name=‘code’ class=‘brush:python’>
def concatena_ficheiros(ficheiro_1,ficheiro_2, ficheiro_3):
with open(ficheiro_1, 'r', encoding='utf-8') as fich_1:
with open(ficheiro_2,'r',encoding='utf-8') as fich_2:
with open(ficheiro_3, 'w',encoding='utf-8') as fich_3:
dados_1 = fich_1.readlines()
dados_2 = fich_2.readlines()
dados_3 = []
for i,linha in enumerate(dados_1):
dados_3 += [linha[:-1] + '\t' + dados_2[i]]
fich_3.writelines(dados_3)
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-62451071458736400582017-12-15T09:54:00.003+00:002017-12-15T09:54:44.444+00:00Teste 3 - TP1 <b>P2</b> <br><br>
Suponha que tem uma <b>árvore genealógica</b> organizada usando um dicionário. A chave é um tuplo com dois elementos, em que cada elemento é um tuplo com o nome do progenitor e respectiva idade. O valor é uma lista com tuplos, em que cada tuplo tem o nome do descendente e respectiva idade. Um exemplo:
<pre name=‘code’ class=‘brush:python>
ag = {(('ernesto',64),('anabela',44)): [('daniela', 15)], (('vitor',72),('irm',68)): [('ines', 39)], (('carlos',68),('mena',65)): [('ricardo', 42), ('marcos', 37)],(('josé',85),('lurdes',82)): [('ernesto', 64), ('afonso',74),('vitor',72), ('carlos',68),('isabel',66)], (('joaobranco',70),('isabel',66)): [('anaisabel', 35),('joana',29)], (('jbp',77),('jbm',70)): [('joaobranco', 70), ('graça',68)]}
</pre>
Escreva um programa que dada uma árvore genealógica e o nome de uma pessoa, devolva o <b>nome do mais velho dos avós</b>. Exemplo:
<pre name=‘code’ class=‘brush:python>
>>> avo_mais_velho(ag,'anaisabel')
josé
</pre>
Os avós de uma pessoa são os pais dos pais. Daí que a solução que vamos apresentar use uma definição auxiliar para determinar quem são os pais de alguém. Relativamente ao que tinha sido feito nas aulas apenas se teve que ter em linha de conta que agora cada pessoa tem associada a respectiva idade.
<pre name=‘code’ class=‘brush:python>
def pais_de(ag,pessoa):
for ch,val in ag.items():
for nome, idade in val:
if nome == pessoa:
return ch
return ()
</pre>
Usando esta função auxiliar, o resto é simples: determinamos quem são os pais da pessoa. se existirem, achamos os pais da cada um dos progenitores e chegamos aos quatro avós. De seguida obtemos o mais velho.
<pre name=‘code’ class=‘brush:python>
def avo_mais_velho(ag,pessoa):
pais = pais_de(ag,pessoa)
if pais:
avos = []
for progenit in pais:
avos.extend(pais_de(ag,progenit[0]))
# mais velho
velho = avos[0]
for av in avos[1:]:
if velho[1] < av[1]:
velho = av
return velho[0]
return None
</pre>
<b>P3</b> <br><br>
Suponha que tem dois ficheiros de texto com exactamente o mesmo número de linhas. Pretende-se que escreva um programa que dados esses dois ficheiros crie um terceiro cujo conteúdo resulta da <b>fusão</b> dos dois primeiros. A fusão é feita alternando as linhas dos dois ficheiros de origem. Assim, se A e B forem os dois ficheiros de origem e C o ficheiro resultado, então C vai ter na primeira linha a primeira linha do ficheiro A, na segunda linha a primeira linha do ficheiro B, na terceira linha a segunda linha do ficheiro A, na quarta linha a segunda linha do ficheiro B, e assim sucessivamente. <br><br>
Problema muito simples. Lemos os ficheiros de entrada como listas de linhas (readlines). De seguida criamos uma nova lista em que os elementos das listas de entrada são colocadas na posição certa. Finalmente, escrevemos a lista de linhas no novo ficheiro. Como diz o enunciado, esta solução supõe o mesmo número del linhas nos dois ficheiros de entrada.
<pre name=‘code’ class=‘brush:python>
def junta_ficheiros_b(ficheiro_1,ficheiro_2, ficheiro_3):
with open(ficheiro_1, 'r', encoding='utf-8') as fich_1:
with open(ficheiro_2,'r',encoding='utf-8') as fich_2:
with open(ficheiro_3, 'w',encoding='utf-8') as fich_3:
dados_1 = fich_1.readlines()
dados_2 = fich_2.readlines()
dados_3 = []
for i in range(len(dados_1)):
dados_3.extend([dados_1[i],dados_2[i]])
fich_3.writelines(dados_3))
</pre>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-63816074189089607002017-12-14T12:00:00.000+00:002017-12-14T12:00:07.886+00:00BINGO!Se pensar primeiro é fundamental em programação, também sabemos que conhecer bem a linguagem em que vamos expressar a solução é igualmente importante. Numa das fichas recente de exercícios vinha a seguinte questão:<br><br>
Os cartões para jogar BINGO têm 5 colunas, cada uma com 5 números. As colunas têm associado as letras B, para a primeira coluna, I para a segunda, N para a terceira, G para a quarta e O para a quinta. Nas primeira colunas os números podem ser entre 1 e 15, na segunda entre 16 e 30, na terceira entre 31 e 45, na quarta entre 46 e 60 e na quinta entre 61 e 75. Escreva um programa que permita gerar e guardar de modo apropriado um cartão de bingo. Escreva um segundo programa que permita visualizar um cartão de bingo.
Vejamos a solução para a primeira questão:
<pre name=‘code’ class=‘brush:python’>
import random
def bingo():
nome = 'BINGO'
cartao = dict()
for i,letra in enumerate(nome):
lista = list(range(i*15 +1, i*15 + 16))
numeros = random.sample(lista,5)
numeros.sort()
cartao[letra] = numeros
return cartao
</pre>
O que tem de especial a solução? Desde logo usamos um <b>dicionário</b> para guardar a representação de um cartão. A chave é uma das letras de ‘BINGO’ e o valor são os 5 números. Usamos o método <b>sample</b> do módulo <b>random</b> para gerar os 5 números. Este método garante que os números serão todos diferentes. Finalmente usamos o método <b>enumerate</b> para poder ter uma forma simples de gerar os diferentes intervalos dos números.<br><br>
A segunda questão era a da visualização do cartão. Aqui vai a solução:
<pre name=‘code’ class=‘brush:python’>
def mostra_bingo(cartao):
numeros_colunas = list(cartao.values())
numeros_linhas = list(zip(*numeros_colunas))
print('%2s\t%2s\t%2s\t%2s\t%2s\t'% tuple('BINGO'))
print('_' * 35)
for linha in numeros_linhas:
print('%2s\t%s\t%s\t%s\t%s\t'% linha)
</pre>
Qual era a dificuldade deste problema? O facto de termos de passar de listas de números associados a letras para uma visualização por colunas. Esta é uma situação onde a operação <b>zip</b> mostra todo o seu esplendor. O facto de fazermos:
<pre name=‘code’ class=‘brush:python’>
numeros_linhas = list(zip(*numeros_colunas))
</pre>
tem a seguinte explicação. Primeiro, <b>zip</b> é um iterador pelo que para obtermos todos os seus elementos de uma vez temos que usar list sobre o resultado de <b>zip</b>. Depois, o <b>asterisco</b> (*) resulta de <b>zip</b> se aplicar a uma sequência de argumentos e <b>numeros_colunas</b> ter apenas um. <b>numeros_colunas</b> é uma lista em que cada elemento é uma lista de 5 listas cada uma com 5 números. Com o <b>asterisco</b> passamos a ter as 5 listas como argumentos do zip.<br><br>
<pre name=‘code’ class=‘brush:python’>
B I N G O
__________________
2 17 33 49 61
5 18 34 50 62
8 26 35 56 66
11 27 37 57 72
13 28 40 60 74
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-54652972002696230692017-12-14T11:16:00.002+00:002017-12-14T11:16:33.882+00:00Pensar primeiro…Programamos para resolver problemas, pelo que o centro da nossa atenção deve ser o problema. Por isso a primeira coisa que devemos fazer é pensar. Pensar para perceber o enunciado: qual é a entrada, qual é a saída. Pensar para definir uma estratégia: posso decompor o problema em sub-problemas mais simples? Já resolvi algum problema semelhante? <br><br>
Este intróito vem a propósito da dificuldade que alguns encontraram em resolver o <b>problema seguinte</b>:
Suponha que tem um dicionário que relaciona receitas com ingredientes. Faça um programa que retorne outro dicionário contendo os ingredientes usados no maior número de receitas, bem como as receitas em que cada um é usado. Por exemplo: <br><br>
<pre name=‘code’ class=‘brush:python’>
>>> receitas={’sonhos’:[’agua’,’farinha’,’manteiga’, ’ovos’,’acucar’],’rabanadas’:[’pao’,’leite’,’ovos ’,’manteiga’,’acucar’],’leite creme’:[’acucar’,’ farinha’,’ovos’,’leite’]}
>>> ingredientes_mais_usados(receitas) {’ovos’: [’sonhos’, ’rabanadas’, ’leite creme’], ’
acucar’: [’sonhos’, ’rabanadas’, ’leite creme’]}
</pre>
Se pensarmos um pouco verificamos que temos que passar de um dicionário em que as chaves são nomes de receitas e os valores são listas de produtos, para um dicionário em que as chaves são produtos e os valores listas de receitas onde esses produtos aparecem. Ou seja: temos que inverter o dicionário. Este é um problema que já resolvemos no passado:
<pre name=‘code’ class=‘brush:python’>
def meu_inverte_dicio(dicio):
novo_dicio = dict()
for ch, val in dicio.items():
for ingrediente in val:
novo_dicio[ingrediente] = novo_dicio.setdefault(ingrediente,[]) + [ch]
return novo_dicio
</pre>
Esta solução mostra a grande vantagem em usar o método <b>setdefault</b>!!<br><br>
A partir do momento que temos o dicionário invertido temos que o percorrer à procura dos elementos cujo valor tem tamanho máximo. Já sabemos como resolver o problema semelhante de encontrar o elemento que é o “maior” de acordo com um dado critério, quando os elementos estão guardados numa lista. Neste caso, o problema é mais complexo por os elementos estarem num dicionário e por querermos não um mas todos os que têm dimensão máxima. Uma solução, possível passa por construir primeiro uma lista e só depois passar a lista a dicionário. <br><br>
<pre name=‘code’ class=‘brush:python’>
def meu_ingredientes_mais_usados(dicio):
dicio_ingredientes = meu_inverte_dicio(dicio)
lista_resultado = []
tamanho = 0
for ch, val in dicio_ingredientes.items():
if len(val) > tamanho:
lista_resultado = [(ch,val)]
tamanho = len(val)
elif len(val) == tamanho:
lista_resultado.append((ch,val))
dicio_resultado = dict(lista_resultado)
return dicio_resultado
</pre>
Mas nada impede que se construa o dicionário final directamente:
<pre name=‘code’ class=‘brush:python’>
def meu_ingredientes_mais_usados_b(dicio):
dicio_ingredientes = meu_inverte_dicio(dicio)
dicio_resultado = dict()
tamanho = 0
for ch, val in dicio_ingredientes.items():
if len(val) > tamanho:
dicio_resultado.clear()
dicio_resultado[ch] = val
tamanho = len(val)
elif len(val) == tamanho:
dicio_resultado[ch] = val
print(dicio_resultado)
return dicio_resultado
</pre>
Como se pode ver, cada vez que encontramos uma solução melhor, limpamos o dicionário (método <b>clear</b>) e actualizamos de seguida.
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-11811858647656668222017-11-22T13:26:00.002+00:002017-11-22T13:26:49.272+00:00Teste # 2 - TP2<b>P2 </b><br><br>
O problema: Duas palavra dizem-se <b>deslocadas</b> de <b>n</b> posições se se puder obter os caracteres de uma deslocando cada caracter da outra <b>n</b> posições no alfabeto. Por exemplo, <i>HAL</i> e <i>IBM</i> são palavras deslocadas com uma distância de 1. Escreva um programa que determina se duas palavras são deslocadas ou não de <b>n</b>. <br><br>
A solução. Definimos o alfabeto e depois procuramos as posições no alfabeto dos caracteres presentes nas duas palavras. É preciso que todas as diferenças tenham o mesmo valor, igual a <b>n</b>. <br><br>
<pre name=‘code’ class=‘brush:python’>
def deslocadas(pal_1, pal_2,n):
"""
pal_1 é pal_2 quand se deslocam todos os caracteres de n posições?
Exemplo: IBM,HAL e n = 1.
"""
alfabeto ='abcdefghijklmnopqrstuvwxyz'
for i in range(len(pal_1)):
indice_1 = alfabeto.find(pal_1[i])
indice_2 = alfabeto.find(pal_2[i])
if (indice_1 - indice_2) != n:
return False
return True
</pre>
Notar que a ordem porque efectuamos a comparação dos índices é relevante. Se <b>n</b> for positivo a primeira palavra deve estar <i>à esquerda</i> da segunda, se for negativo é o contrário. Se quisermos não nos preocupar com a ordem e usar sempre valores positivos de <b>n</b> só temos que acrescentar:
<pre name=‘code’ class=‘brush:python’>
def desloc(p_1,p_2,n):
return (deslocadas(p_1, p_2,n) or deslocadas(p_2, p_1,n)
</pre>
P3 <br><br>
O problema: Suponha que tem duas imagens a preto e branco e pretende saber se uma delas, a mais pequena,<b>ocorre</b> na imagem maior. Escreva um programa que resolve esta questão.<br><br>
A solução. O problema desdobra-se em dois sub-problemas: saber se a operação <b>é possível</b> e, no caso afirmativo, calcular a <b>ocorrência do canto superior esquerdo</b>. Para simplificar a questão criámos um pequeno <b>programa auxiliar</b> que determina se duas imagens são iguais ou não.<br><br>
<pre name=‘code’ class=‘brush:python’>
def ocorre(img_1, img_2):
# possivel
n_linhas_1 = len(img_1)
n_colunas_1 = len(img_1[0])
n_linhas_2 = len(img_2)
n_colunas_2 = len(img_2[0])
possivel = (n_linhas_1 <= n_linhas_2) and (n_colunas_1 <= n_colunas_2)
# verifica
for l in range(n_linhas_2 - n_linhas_1 + 1):
for c in range(n_colunas_2 - n_colunas_1 + 1):
# verifica se imagem 1 ocorre a partir da posição (l,c)
if igual(img_1,img_2,l,c):
return (l,c)
return (-1,-1)
def igual(img_1,img_2,l,c):
for i in range(len(img_1)):
for j in range(len(img_1[0])):
if img_1[i][j] != img_2[l+i][c+j]:
return False
return True
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-87726433160804673912017-11-22T13:12:00.000+00:002017-11-22T13:12:11.926+00:00Teste # 2 - TP1<b>P2</b> <br><br>
O problema: Um palavra diz-se <b>embebida</b> noutra palavra se os seus caracteres ocorrerem nesta pela mesma ordem, embora não necessariamente de modo consecutivo. Por exemplo, <i>ADIA</i> está embebida em <i>ACADEMIA</i>. Escreva um programa que dadas duas palavras, determina se uma delas está embebida na outra.<br><br>
A solução. Não há grande mistério. Analisamos os caracteres da palavra mais pequena e verificamos se está presente na palavra maior. Para garantir o <b>problema da ordem</b> cada vez que fazemos a pesquisa limitarmos o campo de procura de modo que a posição inicial seja a posição imediatamente à frente da posição onde se encontrou o último caractere. notar que mal um caractere não esteja presente o programa termina com False.
<pre name=‘code’ class=‘brush:python’>
def embebida(pal_1, pal_2):
""" verifica se pal_1 está embebida em pal_2."""
pos = 0
for car in pal_1:
indice = pal_2.find(car,pos)
if indice == -1:
return False
pos = indice + 1
return True
</pre>
<b>P3 </b><br><br>
O problema: Suponha que tem duas imagens a preto e branco e pretende que uma delas, a mais pequena, substitua uma parte da imagem maior. Escreva um programa que dadas as duas imagens e o ponto de inserção efectue a modificação. <br><br>
A solução. A solução apresentada tem duas partes: uma em que se verifica se a operação é possível, e a segunda em que se processa a alteração. como se pode ver a estratégia consistiu em percorrer a imagem mais pequena e colocar os seus valores no sítio certo da imagem maior. <br><br>
<pre name=‘code’ class=‘brush:python’>
def altera_img(img_1, img_2,pos_l, pos_c):
""" altera a imagem 2 com a imagem 1 a partir das posição (pos_l,pos_c)"""
# é possível?
n_linhas_1 = len(img_1)
n_colunas_1 = len(img_1[0])
n_linhas_2 = len(img_2)
n_colunas_2 = len(img_2[0])
possivel = ((pos_l + n_linhas_1) <= n_linhas_2) and ((pos_c + n_colunas_1) <= n_colunas_2)
if possivel:
# modifica
for l in range(n_linhas_1):
for c in range(n_colunas_1):
img_2[pos_l + l][pos_c + c] = img_1[l][c]
return img_2
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-34083538118273189432017-11-18T10:54:00.000+00:002017-11-18T10:54:44.543+00:00Variações em torno de vogaisNas aulas discutimos o problema de escrever um programa que <b>leia um texto e indique para cada vogal a lista das suas ocorrências no texto</b>. Trata-se de um problema em que, naturalmente, se opta por um <b>dicionário</b> para representar o resultado. Assim, para:
<pre name=‘code’ class=‘brush:python’ >
txt = 'ernesto ui ui cuidado com os alunos’
</pre>
o resultado deve ser:
<pre name=‘code’ class=‘brush:python’ >
{'e': [0, 3], 'o': [6, 20, 23, 26, 33], 'u': [8, 11, 15, 31], 'i': [9, 12, 16], 'a': [18, 29]}
</pre>
A solução evidente segue o <b>padrão ciclo-acumulador</b>. Aqui o acumulador é o dicionário que vai sendo actualizado à medida que percorremos o texto caractere a caractere.
<pre name=‘code’ class=‘brush:python’ >
def vogais_x(texto):
dicio = dict()
for i in range(len(texto)):
if texto[i] == 'a':
dicio['a'] = dicio.get('a',[]) + [i]
elif texto[i] == 'e':
dicio['e'] = dicio.get('e',[]) + [i]
elif texto[i] == 'i':
dicio['i'] = dicio.get('i',[]) + [i]
elif texto[i] == 'o':
dicio['o'] = dicio.get('o',[]) + [i]
elif texto[i] == 'u':
dicio['u'] = dicio.get('u',[]) + [i]
else:
continue
return dicio
</pre>
Todos concordaremos que é uma solução feia. Todos aqueles <i>ifs</i> podem ser facilmente removidos. Por outro lado, sabemos que vamos precisar não apenas das posições mas também dos caracteres, pelo que nos interessa percorrer o texto obtendo estes dois elementos. Daí uma nova versão:
<pre name=‘code’ class=‘brush:python’ >
def vogais_y(texto):
vogais ='aeiou'
dicio = dict()
for i,car in enumerate(texto):
if texto[i] in vogais:
dicio[car] = dicio.get(car,[]) + [i]
return dicio
</pre>
Estas soluções constroem o dicionário de modo incremental. No entanto, nós sabemos quais são as chaves do dicionário, e sabemos ainda que nunca mudam. Daí , a possibilidade de criar inicialmente o dicionário com as posições todas iguais a listas vazias, recorrendo ao método <i>fromkeys</i>. Como consequência deixamos de necessitar do uso do método <i>get</i>.
<pre name=‘code’ class=‘brush:python’ >
def vogais(texto):
vogais ='aeiou'
dicio = dict.fromkeys(vogais, [])
for i,car in enumerate(texto):
if car in vogais:
dicio[car] = dicio[car] + [i]
return dicio
</pre>
O leitor atento dirá que ainda se pode ter uma solução mais elegante usando uma <b>atribuição aumentada</b>: +=.
<pre name=‘code’ class=‘brush:python’ >
def vogais_z(texto):
vogais ='aeiou'
dicio = dict.fromkeys(vogais, [])
for i,car in enumerate(texto):
if car in vogais:
dicio[car] += [i] # <———
return dicio
</pre>
No entanto, se correr este código verificará que não funciona. Ou melhor, corre mas apresenta o resultado errado:
<pre name=‘code’ class=‘brush:python’ >
{'a': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'e': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'i': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'o': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33], 'u': [0, 3, 6, 8, 9, 11, 12, 15, 16, 18, 20, 23, 26, 29, 31, 33]}
</pre>
A explicação para o resultado (todas as vogais ocorrem nas mesmas posições, igual à união das posições de cada uma…) é simples: a construção do dicionário usando <i>fromkeys</i> faz com que os valores iniciais sejam o mesmo objecto (partilha de memória) e a instrução <b>+=</b> não constrói objectos novos. Uma solução consiste em alterar a construção do dicionário inicial, usando <b>dicionários por compreensão</b>:
<pre name=‘code’ class=‘brush:python’ >
def vogais_e(texto):
vogais ='aeiou'
dicio = {vog:[] for vog in 'aeiou'}
for i,car in enumerate(texto):
if car in vogais:
dicio[car] += [i]
return dicio
</pre>
E pronto. Espero que tenha entendido. Ah, já agora, ainda outra solução….
<pre name=‘code’ class=‘brush:python’ >
def vogais_d(texto):
vogais ='aeiou'
dicio = {vog:[] for vog in 'aeiou'}
for i,car in enumerate(texto):
if car in vogais:
dicio[car].append(i)
return dicio
</pre>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-22034451638672508982017-10-23T15:41:00.000+01:002017-10-23T15:41:19.624+01:00Teste #1 - TP4
P1<br><br>
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.
<pre name=‘code’ class=‘brush:python’>
def polar_to_cart_x(r, ang):
x = r * math.cos(ang)
return x
if __name__ == “__main__":
print(polar_to_cart_x (10, pi/4))
</pre>
Não é feita a importação do módulo <b>math</b> e devemos usar <b>math.pi</b> e não <b>pi</b>. <br><br>
P2<br><br>
Escreva um programa para contar o número de pontos que estão dentro de uma circunferência de raio <b>r</b> centrada em (0, 0). O programa deverá receber o número de pontos atestar (<b>num</b>) e o raio (<b>r</b>) 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.
<pre name=‘code’ class=‘brush:python’>
import math
def dentro(raio, x, y):
len = math.sqrt(x ** 2 + y ** 2)
if len <= raio:
return True
else:
return False
def conta_dentro_circulo(num, raio):
num_dentro = 0
for i in range(num):
x = eval(input("x: "))
y = eval(input("y: "))
if dentro(raio, x, y):
num_dentro = num_dentro + 1
return num_dentro
if __name__ == "__main__":
print(conta_dentro_circulo(3, 20))
</pre>
P3<br><br>
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.
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTfBjJL3zcA656J2QitvY-lU3P81FjMI1hDy0FivJxkvoQafa1t3jHNRtHnA46YXTfNhymJIlXyPK1JRuMu1lY0LpTPMeUmoYEiEQ2-XssHf8uTCNLTlzR4xiCxnrfAcfMkgZ4yF9LmM0d/s1600/teste1_p3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTfBjJL3zcA656J2QitvY-lU3P81FjMI1hDy0FivJxkvoQafa1t3jHNRtHnA46YXTfNhymJIlXyPK1JRuMu1lY0LpTPMeUmoYEiEQ2-XssHf8uTCNLTlzR4xiCxnrfAcfMkgZ4yF9LmM0d/s320/teste1_p3.png" width="320" height="218" data-original-width="471" data-original-height="321" /></a></div>
<pre name=‘code’ class=‘brush:python’>
import turtle
import random
def rectangulo(xr, yr, lado1, lado2):
#preparar
turtle.penup()
turtle.goto(xr,yr)
turtle.pendown()
#desenhar
for i in range(4):
if i%2 == 0:
turtle.forward(lado1)
else:
turtle.forward(lado2)
turtle.right(90)
def circulo(xc, yc, raio):
#preparar
turtle.penup()
turtle.goto(xc, yc)
turtle.pendown()
#desenhar
turtle.circle(raio)
def forma_rect_circulo(xf, yf, lado1, lado2):
#rectangulo
rectangulo(xf, yf, lado1, lado2)
#circulo no interior
raio = min(lado1, lado2) / 2
xc = xf + lado1/2
yc = yf - lado2/2 - raio
circulo(xc, yc, raio)
def desenha_formas(num, xmin, xmax, ymin, ymax, ladomin, ladomax):
#sortear e desenhar
for i in range(num):
xf = random.randint(xmin, xmax)
yf = random.randint(ymin, ymax)
lado1 = random.randint(ladomin, ladomax)
lado2 = random.randint(ladomin, ladomax)
forma_rect_circulo(xf, yf, lado1, lado2)
if __name__ == "__main__":
turtle.hideturtle()
desenha_formas(5, -300, 300, -300, 300, 50, 150)
turtle.exitonclick()
</pre>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-4882844481606334722017-10-22T11:20:00.000+01:002017-10-22T11:20:04.045+01:00Exercícios de Programação Descendente (II)
Nas aulas foi colocado o problema de visualizar na forma de um <b>histograma</b> 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. <br><br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN23kY8v5Qi8hAwt38MgL5s9adXma1O5G9ILFpzrbP5Bi78fwjhIzibIG474JEZU3WcTQ0q937bDUKNux30xDRBMTGhV79NmceN7_OIk9Sd9CY6_jBQ6uqEnmpekefCuIWZxfadZAsJlFb/s1600/histo_lanca.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN23kY8v5Qi8hAwt38MgL5s9adXma1O5G9ILFpzrbP5Bi78fwjhIzibIG474JEZU3WcTQ0q937bDUKNux30xDRBMTGhV79NmceN7_OIk9Sd9CY6_jBQ6uqEnmpekefCuIWZxfadZAsJlFb/s400/histo_lanca.png" width="400" height="148" data-original-width="566" data-original-height="210" /></a></div>
Vamos novamente tentar perceber se podemos <b>dividir o problema em sub-problemas</b> 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 <b>turtle</b> para a visualização! É clara a existência de uma <b>dependência</b> 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 <b>n</b> para construir um <b>tuplo</b> <b>(n1,n2,n3,n4,n5,n6)</b>, com <b>ni</b> igual ao número de vezes que saiu o número <b>i</b>, e <b>n1+n2+n3+n4+n5+n6 = n</b>. A escolha de um tuplo é natural pois precisamos de um contentor, com a função de <b>memória</b>. Tomadas estas decisões, podemos passar a concretização do programa.
<pre name=‘code’ class=‘brush:python’>
import turtle
def dados_histo(n):
# experiência de lançamentos
res = lanca_dado(n)
# visualizaçao
histograma(res)
def lanca_dado(n):
pass
def histograma(res):
pass
if __name__ == '__main__':
n = 100
dados_histo(n)
</pre>
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.
<pre name=‘code’ class=‘brush:python’>
import turtle
def dados_histo(n):
# experiência de lançamentos
res = lanca_dado(n)
# visualizaçao
histograma(res)
def lanca_dado(n):
pass
def histograma(res):
posx = -100
posy = 0
comp_linha = 80
for i,alt in enumerate(res):
# desenha caso i
desenha(i+1,alt,posx,posy,comp_linha)
# define parâmetros
posx = posx + comp_linha
def desenha(i,alt, posx,posy,comp_linha):
pass
if __name__ == '__main__':
n = 100
teste = (46,39,105,0,44,5)
histograma(teste)
#dados_histo(n)
</pre>
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 <b>enumerate</b>. 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 <b>centro</b> 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 <b>vantagens da programação descendente</b>: 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.
<pre name=‘code’ class=‘brush:python’>
def desenha(i,alt, posx,posy,comp_linha):
# escreve número i
# desenha linha
# desenha coluna
# escreve número de vezes (alt) que saiu o número i
pass
</pre>
Podemos resolver cada um destes três sub-problemas e testá-los isoladamente.
<pre name=‘code’ class=‘brush:python’>
def coluna(posx,posy, lado_1, lado_2,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
# desenha
turtle.color(cor)
turtle.begin_fill()
for i in range(2):
turtle.forward(lado_1)
turtle.left(90)
turtle.forward(lado_2)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def linha(posx,posy,comp_linha,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# linha
turtle.forward(comp_linha)
turtle.hideturtle()
def escreve_numero(posx,posy,fonte,valor,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# escreve
turtle.write(valor,font=fonte)
turtle.hideturtle()
</pre>
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, <b>decidimos</b> 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.
<pre name=‘code’ class=‘brush:python’>
def desenha(i,alt, posx,posy,comp_linha):
# escreve número i
escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')
# desenha linha
linha(posx,posy,comp_linha,'red')
# desenha coluna
coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
# escreve número de vezes (alt) que saiu o número i
escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
</pre>
Podemos agora testar o problema de visualizar. E está na hora de ver tudo junto.
<pre name=‘code’ class=‘brush:python’>
import turtle
def dados_histo(n):
# experiência de lançamentos
res = lanca_dado(n)
# visualizaçao
histograma(res)
def lanca_dado(n):
pass
def histograma(res):
posx = -100
posy = 0
comp_linha = 80
for i,alt in enumerate(res):
# desenha caso i
desenha(i+1,alt,posx,posy,comp_linha)
# define parâmetros
posx = posx + comp_linha
def desenha(i,alt, posx,posy,comp_linha):
# escreve número i
escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')
# desenha linha
linha(posx,posy,comp_linha,'red')
# desenha coluna
coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
# escreve número de vezes (alt) que saiu o número i
escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
def coluna(posx,posy, lado_1, lado_2,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
# desenha
turtle.color(cor)
turtle.begin_fill()
for i in range(2):
turtle.forward(lado_1)
turtle.left(90)
turtle.forward(lado_2)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def linha(posx,posy,comp_linha,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# linha
turtle.forward(comp_linha)
turtle.hideturtle()
def escreve_numero(posx,posy,fonte,valor,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# escreve
turtle.write(valor,font=fonte)
turtle.hideturtle()
if __name__ == '__main__':
n = 100
teste = (46,39,105,0,44,5)
fonte = ('Arial', 24, 'bold')
posx = 0
posy = 0
cor_1 = 'black'
cor_2 = 'red'
comp = 80
lado_1 = comp/2
lado_2 = 50
#escreve_numero(posx,posy,fonte,n,cor)
#linha(posx,posy,comp,cor_2)
#coluna(posx,posy, lado_1, lado_2,cor_2)
#desenha(4,100, posx,posy,comp)
histograma(teste)
#dados_histo(n)
turtle.exitonclick()
</pre>
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:
<pre name=‘code’ class=‘brush:python’>
def lanca_dado(n):
# lançamento
resultado = tuple()
for i in range(n):
numero = random.randint(1,6)
resultado = resultado + (numero,)
# contagem
conta = tuple()
for i in range(1,7):
conta_i = resultado.count(i)
conta = conta + (conta_i,)
return conta
</pre>
Podemos finalmente testar o programa completo.
<pre name=‘code’ class=‘brush:python’>
import turtle
import random
def dados_histo(n):
# experiência de lançamentos
res = lanca_dado(n)
# visualizaçao
histograma(res)
def lanca_dado(n):
# lançamento
resultado = tuple()
for i in range(n):
numero = random.randint(1,6)
resultado = resultado + (numero,)
# contagem
conta = tuple()
for i in range(1,7):
conta_i = resultado.count(i)
conta = conta + (conta_i,)
return conta
def histograma(res):
posx = -100
posy = 0
comp_linha = 80
for i,alt in enumerate(res):
# desenha caso i
desenha(i+1,alt,posx,posy,comp_linha)
# define parâmetros
posx = posx + comp_linha
def desenha(i,alt, posx,posy,comp_linha):
# escreve número i
escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')
# desenha linha
linha(posx,posy,comp_linha,'red')
# desenha coluna
coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
# escreve número de vezes (alt) que saiu o número i
escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
def coluna(posx,posy, lado_1, lado_2,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
# desenha
turtle.color(cor)
turtle.begin_fill()
for i in range(2):
turtle.forward(lado_1)
turtle.left(90)
turtle.forward(lado_2)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def linha(posx,posy,comp_linha,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# linha
turtle.forward(comp_linha)
turtle.hideturtle()
def escreve_numero(posx,posy,fonte,valor,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# escreve
turtle.write(valor,font=fonte)
turtle.hideturtle()
if __name__ == '__main__':
n = 100
teste = (46,39,105,0,44,5)
fonte = ('Arial', 24, 'bold')
posx = 0
posy = 0
cor_1 = 'black'
cor_2 = 'red'
comp = 80
lado_1 = comp/2
lado_2 = 50
#escreve_numero(posx,posy,fonte,n,cor)
#linha(posx,posy,comp,cor_2)
#coluna(posx,posy, lado_1, lado_2,cor_2)
#desenha(4,100, posx,posy,comp)
#histograma(teste)
#print(lanca_dado(n))
dados_histo(n)
turtle.exitonclick()
</pre>
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?? <br><br>
Na sala, alguns disseram que <b>nos histogramas as colunas não estão separadas</b>. 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 <b>desenha</b> para que as colunas fiquem claramente a ver-se e, na função <b>histograma</b>, alterar o posicionamento ao longo do eixo dos xx da cada coluna.
<pre name=‘code’ class=‘brush:python’>
import turtle
import random
def dados_histo(n):
# experiência de lançamentos
res = lanca_dado(n)
# visualizaçao
histograma(res)
def lanca_dado(n):
# lançamento
resultado = tuple()
for i in range(n):
numero = random.randint(1,6)
resultado = resultado + (numero,)
# contagem
conta = tuple()
for i in range(1,7):
conta_i = resultado.count(i)
conta = conta + (conta_i,)
return conta
def histograma(res):
posx = -100
posy = 0
comp_linha = 80
for i,alt in enumerate(res):
# desenha caso i
desenha(i+1,alt,posx,posy,comp_linha)
# define parâmetros
posx = posx + comp_linha/2
def desenha(i,alt, posx,posy,comp_linha):
# escreve número i
escreve_numero(posx + comp_linha/2,posy - 15,('Arial',12,'bold'),i,'black')
# desenha coluna
coluna(posx+comp_linha/4,posy,comp_linha/2,alt,'red')
# escreve número de vezes (alt) que saiu o número i
escreve_numero(posx + comp_linha/2 - 5,posy + alt ,('Arial',12,'bold'),alt,'black')
def coluna(posx,posy, lado_1, lado_2,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
# desenha
turtle.pencolor('black')
turtle.fillcolor(cor)
turtle.begin_fill()
for i in range(2):
turtle.forward(lado_1)
turtle.left(90)
turtle.forward(lado_2)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def linha(posx,posy,comp_linha,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# linha
turtle.forward(comp_linha)
turtle.hideturtle()
def escreve_numero(posx,posy,fonte,valor,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.color(cor)
turtle.pendown()
# escreve
turtle.write(valor,font=fonte)
turtle.hideturtle()
if __name__ == '__main__':
n = 1000
teste = (46,39,105,0,44,5)
fonte = ('Arial', 24, 'bold')
posx = 0
posy = 0
cor_1 = 'black'
cor_2 = 'red'
comp = 80
lado_1 = comp/2
lado_2 = 50
#escreve_numero(posx,posy,fonte,n,cor)
#linha(posx,posy,comp,cor_2)
#coluna(posx,posy, lado_1, lado_2,cor_2)
#desenha(4,100, posx,posy,comp)
#histograma(teste)
#print(lanca_dado(n))
dados_histo(n)
turtle.exitonclick()
</pre>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaKhCWfE8kN-h496G14xZK3ILj6Xm7FLSRyWD3d6yZM57699jJdvYhrM5vPAR7IzUmQJv9z4_KjI1PMsVEKBErLTwEvGIzR-Xv-mBT2lf0SZ_f10vAkdlC2I4w-f9dXU1keRNrJ9K0E4q-/s1600/histo_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaKhCWfE8kN-h496G14xZK3ILj6Xm7FLSRyWD3d6yZM57699jJdvYhrM5vPAR7IzUmQJv9z4_KjI1PMsVEKBErLTwEvGIzR-Xv-mBT2lf0SZ_f10vAkdlC2I4w-f9dXU1keRNrJ9K0E4q-/s400/histo_3.png" width="400" height="334" data-original-width="329" data-original-height="275" /></a></div>
Para o leitor: e se quisermos que o histograma possa ter uma orientação qualquer??
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-5812501410453672212017-10-21T23:19:00.003+01:002017-10-21T23:19:42.101+01:00Verdades...<b>Pense nisto ...</b><br><br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6C_EQ4dwqeoqRPgnF2SrZJmazyhn2TglZz20SbO4wJBoluseCBFa6MnI9axC8LmtafFuqqQA6F3YzuPLmmJxeieTt92guo0gcdHmTX9JHSLzzTJuGfDHNJdfXvZS4-kzRMMs5KFl6yQzz/s1600/prog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6C_EQ4dwqeoqRPgnF2SrZJmazyhn2TglZz20SbO4wJBoluseCBFa6MnI9axC8LmtafFuqqQA6F3YzuPLmmJxeieTt92guo0gcdHmTX9JHSLzzTJuGfDHNJdfXvZS4-kzRMMs5KFl6yQzz/s320/prog.png" width="320" height="285" data-original-width="793" data-original-height="707" /></a></div>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-86378926545198104702017-10-21T11:50:00.001+01:002017-10-21T11:50:25.290+01:00Exercí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.
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjd_zWzcl03FsOvOhYVVCndjcdL6hQgmH3WvLKN3iEb1-RKjz-o78XkFRiLTn7LBTDzPSidSGklt8CAOw1Xxn4Nk8TfMS0scPL_5_S8E1zL20WYd7T9gSLb_9dxb3t-L0iuD85qzJKh6Lw/s1600/rad_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjd_zWzcl03FsOvOhYVVCndjcdL6hQgmH3WvLKN3iEb1-RKjz-o78XkFRiLTn7LBTDzPSidSGklt8CAOw1Xxn4Nk8TfMS0scPL_5_S8E1zL20WYd7T9gSLb_9dxb3t-L0iuD85qzJKh6Lw/s320/rad_1.png" width="320" height="306" data-original-width="500" data-original-height="478" /></a></div>
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. <br><br>
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.
<pre name=‘code’ class=‘brush:python’>
import turtle
def radioactividade():
# desenha quadrado
quadrado()
# desenha sectores
sectores()
#desenha circunferência
circunferencia()
def quadrado():
pass
def sectores():
pass
def circunferencia():
pass
if __name__ == '__main__':
radioactividade()
</pre>
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.
<pre name=‘code’ class=‘brush:python’>
import turtle
def radioactividade(lado,posx,posy,orientacao,cor):
# desenha quadrado
quadrado(lado,posx,posy,orientacao,cor)
# desenha sectores
sectores()
#desenha circunferência
circunferencia()
def quadrado(lado,posx,posy,orientacao,cor):
turtle.up()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
for i in range(4):
turtle.forward(lado)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def sectores():
pass
def circunferencia():
pass
if __name__ == '__main__':
lado = 100
posx = -50
posy = -50
orientacao = 30
cor = 'yellow'
radioactividade(lado,posx,posy,orientacao,cor)
</pre>
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!<br><br>
Resolvida esta questão vamos escolher um dos dois sub-problemas restantes. Optamos pela circunferência. Também aqui já sabemos o que fazer.
<pre name=‘code’ class=‘brush:python’>
def circunferencia(raio,posx,posy,orienta,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orienta)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
turtle.circle(raio)
turtle.end_fill()
turtle.hideturtle()
</pre>
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.
<pre name=‘code’ class=‘brush:python’>
import turtle
def radioactividade(lado,posx,posy,orientacao,cor, cor_c):
# desenha quadrado
quadrado(lado,posx,posy,orientacao,cor)
# desenha sectores
sectores()
#desenha circunferência
raio = lado/10
posx_c = posx + lado/2 + raio
posy_c = posy + lado/2
orientacao_c = orientacao + 90
circunferencia(raio,posx_c,posy_c,orientacao_c,cor_c)
def quadrado(lado,posx,posy,orientacao,cor):
turtle.up()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
for i in range(4):
turtle.forward(lado)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def sectores():
pass
def circunferencia(raio,posx,posy,orienta,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orienta)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
turtle.circle(raio)
turtle.end_fill()
turtle.hideturtle()
if __name__ == '__main__':
lado = 100
posx = -50
posy = -50
orientacao = 0
cor = 'yellow'
raio = lado/10
cor_c = 'black'
#circunf(raio,posx,posy,orientacao,cor)
radioactividade(lado,posx,posy,orientacao,cor, cor_c)
turtle.exitonclick()
</pre>
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 <b>zero graus</b>, pois é isto que nos é pedido. Caso o valor seja diferente o posicionamento do centro da circunferência também é diferente. <br><br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6aFJ5IPGVPHqaNvn06ww5rTF5n3GUX2hdtUBXj6_Fp3ErW9X6e49ycDFBIWcGwGrnJbtb15gDowhJ7oV8PrV9vTtcU2CBepmiH5vK-_TVaxsyR2u0PGXkDaDghV0rHeqPFme8MRszOO6U/s1600/quad_circ.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6aFJ5IPGVPHqaNvn06ww5rTF5n3GUX2hdtUBXj6_Fp3ErW9X6e49ycDFBIWcGwGrnJbtb15gDowhJ7oV8PrV9vTtcU2CBepmiH5vK-_TVaxsyR2u0PGXkDaDghV0rHeqPFme8MRszOO6U/s320/quad_circ.png" width="320" height="299" data-original-width="300" data-original-height="280" /></a></div>
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.
<pre name=‘code’ class=‘brush:python’>
def sector(raio,posx,posy,orientacao,cor,amplitude):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.color(cor)
turtle.pendown()
turtle.begin_fill()
turtle.forward(raio)
turtle.left(90)
turtle.circle(raio,amplitude)
turtle.left(90)
turtle.forward(raio)
turtle.end_fill()
turtle.setheading(orientacao)
turtle.hideturtle()
</pre>
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.<br><br>
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???
<pre name=‘code’ class=‘brush:python’>
def sectores(raio,posx,posy,orientacao,cor,amplitude):
for i in range(3):
sector(raio,posx,posy,orientacao,cor,amplitude)
orientacao = orientacao + 120
</pre>
Para terminar o trabalho temos que incorporar esta solução no programa principal.
<pre name=‘code’ class=‘brush:python’>
import turtle
def radioactividade(lado,posx,posy,orientacao,cor, cor_c, cor_s):
# desenha quadrado
quadrado(lado,posx,posy,orientacao,cor)
# desenha sectores
raio_s = lado/4
posx_s = posx + lado/2
posy_s = posy + lado/2
orientacao_s = orientacao
amplitude = 60
sectores(raio_s,posx_s,posy_s,orientacao_s,cor_s,amplitude)
#desenha circunferência
raio = lado/10
posx_c = posx + lado/2 + raio
posy_c = posy + lado/2
orientacao_c = orientacao + 90
circunferencia(raio,posx_c,posy_c,orientacao_c,cor_c)
def quadrado(lado,posx,posy,orientacao,cor):
turtle.up()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
for i in range(4):
turtle.forward(lado)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def sectores(raio,posx,posy,orientacao,cor,amplitude):
for i in range(3):
sector(raio,posx,posy,orientacao,cor,amplitude)
orientacao = orientacao + 120
def sector(raio,posx,posy,orientacao,cor,amplitude):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.color(cor)
turtle.pendown()
turtle.begin_fill()
turtle.forward(raio)
turtle.left(90)
turtle.circle(raio,amplitude)
turtle.left(90)
turtle.forward(raio)
turtle.end_fill()
turtle.setheading(orientacao)
turtle.hideturtle()
def circunferencia(raio,posx,posy,orienta,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orienta)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
turtle.circle(raio)
turtle.end_fill()
turtle.hideturtle()
if __name__ == '__main__':
lado = 100
posx = -50
posy = -50
orientacao = 0
cor = 'yellow'
raio = lado/10
cor_c = 'black'
raio_s = lado/4
cor_s = 'black'
amplitude = 60
#circunferencia(raio,posx,posy,orientacao,cor)
#sector(raio_s,posx,posy,orientacao,cor_s,amplitude)
#sectores(raio_s,posx,posy,orientacao,cor_s,amplitude)
radioactividade(lado,posx,posy,orientacao,cor, cor_c,cor_s)
turtle.exitonclick()
</pre>
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.<br><br>
<pre name=‘code’ class=‘brush:python’>
def circunferencia(raio,posx,posy,orienta,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orienta)
turtle.pencolor('white')
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
turtle.circle(raio)
turtle.end_fill()
turtle.hideturtle()
</pre>
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.
<pre name=‘code’ class=‘brush:python’>
import turtle
import math
def radioactividade(lado,posx,posy,orientacao,cor, cor_c, cor_s):
# desenha quadrado
quadrado(lado,posx,posy,orientacao,cor)
# desenha sectores
raio_s = lado/4
ang_base = (orientacao* math.pi / 180)
ang = ang_base + math.pi/4
posx_s = posx + lado/math.sqrt(2) * math.cos(ang)
posy_s = posy + lado/math.sqrt(2) * math.sin(ang)
orientacao_s = orientacao
amplitude = 60
sectores(raio_s,posx_s,posy_s,orientacao_s,cor_s,amplitude)
#desenha circunferência
raio = lado/10
posx_c = posx_s + raio * math.cos(ang_base)
posy_c = posy_s + raio * math.sin(ang_base)
orientacao_c = orientacao + 90
circunferencia(raio,posx_c,posy_c,orientacao_c,cor_c)
def quadrado(lado,posx,posy,orientacao,cor):
turtle.up()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
for i in range(4):
turtle.forward(lado)
turtle.left(90)
turtle.end_fill()
turtle.hideturtle()
def sectores(raio,posx,posy,orientacao,cor,amplitude):
for i in range(3):
sector(raio,posx,posy,orientacao,cor,amplitude)
orientacao = orientacao + 120
def sector(raio,posx,posy,orientacao,cor,amplitude):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orientacao)
turtle.color(cor)
turtle.pendown()
turtle.begin_fill()
turtle.forward(raio)
turtle.left(90)
turtle.circle(raio,amplitude)
turtle.left(90)
turtle.forward(raio)
turtle.end_fill()
turtle.setheading(orientacao)
turtle.hideturtle()
def circunferencia(raio,posx,posy,orienta,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.setheading(orienta)
turtle.pencolor('white')
turtle.pendown()
turtle.fillcolor(cor)
turtle.begin_fill()
turtle.circle(raio)
turtle.end_fill()
turtle.hideturtle()
if __name__ == '__main__':
lado = 200
posx = -50
posy = -50
orientacao = 45
cor = 'yellow'
raio = lado/10
cor_c = 'black'
raio_s = lado/4
cor_s = 'black'
amplitude = 60
radioactividade(lado,posx,posy,orientacao,cor, cor_c,cor_s)
turtle.exitonclick()
</pre>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp7YxndUb-Xj_ROqUDYOaA0GkSP1Ok1IMKMWij5GWjf4Ug4JUQUt1hfOTsfU5zoD7niwwY-4I4NhlVISkD0Gdgx5iqzDadsdzGcttkhzWht9DnKIY7O-a8YQg3Xu-hQqg-Ijyo592_CaJi/s1600/rodado.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjp7YxndUb-Xj_ROqUDYOaA0GkSP1Ok1IMKMWij5GWjf4Ug4JUQUt1hfOTsfU5zoD7niwwY-4I4NhlVISkD0Gdgx5iqzDadsdzGcttkhzWht9DnKIY7O-a8YQg3Xu-hQqg-Ijyo592_CaJi/s320/rodado.png" width="320" height="290" data-original-width="716" data-original-height="648" /></a></div>
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!
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-68414625729921126402017-10-13T16:46:00.001+01:002017-10-13T16:46:26.254+01:00Teste # 1 - TP2<b>P1</b> <br><br>
O que aparece no lugar do ponto de interrogação?
<pre name=‘code’ class=‘brush:python’>
>>> x = 'abacadabra'
>>> x[1] = 'zeus'
Traceback (most recent call last):
Python Shell, prompt 2, line 1
builtins.TypeError: 'str' object does not support item assignment
</pre>
Aparece um erro pois as cadeias de caracteres são imutáveis não podendo o seu valor ser alterado.<br><br>
<b>P2</b> <br><br>
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.
<pre name=‘code’ class=‘brush:python’>
def enviesada_a(n):
# lança e conta
conta = 0
for i in range(n):
conta = conta + random.randint(0,1)
# analisa
return conta != n//2
</pre>
Esta versão baseia-se num padrão dec programação conhecido por <b>ciclo - acumulador</b>. 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. <br><br>
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.
<pre name=‘code’ class=‘brush:python’>
def enviesada(n,perc):
"""
n = número de lançamentos
perc = percentagem aceitável [0,1]
"""
# lança e conta
conta = 0
for i in range(n):
conta = conta + random.randint(0,1)
# analisa
espera = n/2
inf_ = (1 - perc)* espera
sup_ = (1 + perc) * espera
return (conta < inf_) or (conta > sup_)
</pre>
Como se observa usamos agora uma divisão de floats.<br><br>
<b>P3</b> <br><br>
Queremos desenhar bonecos como o da figura.
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOlO2EIbz1rEStNYcjYopx9m5h7uv1CUYfcgqSThvjo2nY4a0wDTBCmglEuHb_xpoAhXUTshoe5fPRlwtbSIikxh7b_oE8Azd1jRttXqxLJxCSscUR9eyngDqxVCz0rxaze3PqLF8rZG1-/s1600/balao3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOlO2EIbz1rEStNYcjYopx9m5h7uv1CUYfcgqSThvjo2nY4a0wDTBCmglEuHb_xpoAhXUTshoe5fPRlwtbSIikxh7b_oE8Azd1jRttXqxLJxCSscUR9eyngDqxVCz0rxaze3PqLF8rZG1-/s320/balao3.png" width="235" height="320" data-original-width="431" data-original-height="587" /></a></div>
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:
<pre name=‘code’ class=‘brush:python’>
def boneco(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
# desenha balão grande
# desenha balão pequeno
# desenha cauda
pass
</pre>
Tratemos dos balões isoladamente:
<pre name=‘code’ class=‘brush:python’>
def bola(raio, posx,posy, orientacao, cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
turtle.setheading(orientacao)
turtle.fillcolor(cor)
turtle.begin_fill()
turtle.circle(raio)
turtle.end_fill()
turtle.hideturtle()
</pre>
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:
<pre name=‘code’ class=‘brush:python’>
def cauda(n, tipo, posx, posy,orientacao, comp, cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
turtle.setheading(orientacao)
turtle.pencolor(cor)
for i in range(n):
turtle.forward(comp)
turtle.right(tipo * 60)
turtle.hideturtle()
</pre>
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.<br><br>
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 é:
<pre name=‘code’ class=‘brush:python’>
def boneco(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
# desenha balão grande
bola(raio_1, posx,posy, orientacao, cor_1)
# desenha balão pequeno
turtle.penup()
turtle.setheading(orientacao-90)
turtle.forward(2*raio_2)
turtle.setheading(orientacao)
turtle.pendown()
bola(raio_2, turtle.xcor(),turtle.ycor(), orientacao, cor_2)
# desenha cauda
</pre>
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:<br><br>
<pre name=‘code’ class=‘brush:python’>
def balao(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
bola(raio_1, posx,posy, orientacao, cor_1)
bola(-raio_2, turtle.xcor(),turtle.ycor(), orientacao, cor_2)
</pre>
Percebe a diferença???<br><br>
Só falta acrescentar a cauda…
<pre name=‘code’ class=‘brush:python’>
def balao(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
bola(raio_1, posx,posy, orientacao, cor_1)
turtle.penup()
turtle.setheading(orientacao-90)
turtle.forward(2*raio_2)
turtle.setheading(orientacao)
turtle.pendown()
bola(raio_2, turtle.xcor(),turtle.ycor(), orientacao, cor_2)
for i in range(n):
cauda(3,(-1)**i,turtle.xcor(),turtle.ycor(),turtle.heading(),20,cor_3)
turtle.hideturtle()
</pre>
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! <br><br>
Pode usar este programa para criar variantes. Por exemplo:<br><br>
<pre name=‘code’ class=‘brush:python’>
def baloes(n,raio_1, raio_2, posx,posy, orientacao, cor_1,cor_2,cor_3):
# balão grande
bola(raio_1, posx,posy, orientacao, cor_1)
# n balões pequenos à volta do balão grande...
for i in range(n):
turtle.penup()
turtle.circle(raio_1,360/n)
turtle.pendown()
bola(-raio_2, turtle.xcor(),turtle.ycor(), turtle.heading(), cor_2)
turtle.hideturtle()
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-46269160007807472792017-10-13T12:54:00.000+01:002017-10-13T12:54:01.460+01:00Teste # 1 - TP1<b>P1</b> <br><br>
Quando fazemos :
<pre name=‘code’ class=‘brush:python’>
>>> X = X + 1
</pre>
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. <br><br>
<b>P2</b> <br><br>
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: <b>ciclo - acumulador</b>. <br><br>
<pre name=‘code’ class=‘brush:python’>
def par_impar(n):
conta_par = 0 # o acumulador
for i in range(n):
num = lanca_dado()
# actualiza o acumulador
# define resultado
</pre>
A implementação da simulação do lançamento do dado é trivial:
<pre name=‘code’ class=‘brush:python’>
import random
def lanca_dado():
return random.randint(1,6)
</pre>
Com estes elementos chegamos facilmente à versão final:
<pre name=‘code’ class=‘brush:python’>
import random
def lanca_dado():
return random.randint(1,6)
def par_impar(n):
conta_par = 0
for i in range(n):
num = lanca_dado()
if (num == 2) or (num == 4) or (num == 6): # if num in (2,4,6):
conta_par = conta_par + 1
if conta_par > n/2:
return True
else:
return False
</pre>
Notar que o teste de saída de número par pode ser abreviado para <i>if num in (2,4,6):</i> <br><br>
<b>P3</b> <br><br>
Queremos um programa que nos permita desenhar figuras como a abaixo. <br><br>
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. <br><br>
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.<br><br>
Comecemos pelos triângulos coloridos, algo que fizemos nas aulas.
<pre name=‘code’ class=‘brush:python’>
def tri_cor(posx,posy,orientacao,lado,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
turtle.setheading(orientacao)
turtle.fillcolor(cor)
turtle.begin_fill()
for i in range(3):
turtle.forward(lado)
turtle.left(120)
turtle.end_fill()
turtle.hideturtle()
</pre>
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. <br><br>
<pre name=‘code’ class=‘brush:python’>
def laco(posx,posy,orientacao,lado,cor):
tri_cor(posx,posy,orientacao,lado,cor)
tri_cor(posx,posy,orientacao + 180,lado,cor)
turtle.hideturtle()
</pre>
Passemos à cauda como sequência de segmentos. Desenhar um segmento com uma dada inclinação é trivial. <br><br>
<pre name=‘code’ class=‘brush:python’>
def seg(posx,posy,comp,orientacao,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
turtle.setheading(orientacao)
turtle.pencolor(cor)
turtle.forward(comp)
turtle.hideturtle()
</pre>
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: <br><br>
<pre name=‘code’ class=‘brush:python’>
def boneco(n, posx, posy, orientacao,inc,comp,lado,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
for i in range(n):
if i % 2 == 0:
seg(turtle.xcor(),turtle.ycor(),comp,orientacao + inc,cor)
laco(turtle.xcor(),turtle.ycor(),turtle.heading()-90,lado,cor)
else:
seg(turtle.xcor(),turtle.ycor(),comp,orientacao - inc,cor)
laco(turtle.xcor(),turtle.ycor(),turtle.heading()+90,lado,cor)
if n % 2 == 0:
seg(turtle.xcor(),turtle.ycor(),comp,orientacao + inc,cor)
else:
seg(turtle.xcor(),turtle.ycor(),comp,orientacao - inc,cor)
</pre>
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! <br><br>
Depois de feito o programa, torna-se evidente que podemos simplificar o desenho dos segmentos:
<pre name=‘code’ class=‘brush:python’>
def boneco(n, posx, posy, orientacao,inc,comp,lado,cor):
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
turtle.pencolor(cor)
for i in range(n):
if i % 2 == 0:
turtle.setheading(orientacao + inc)
turtle.forward(comp)
laco(turtle.xcor(),turtle.ycor(),turtle.heading()-90,lado,cor)
else:
turtle.setheading(orientacao - inc)
turtle.forward(comp)
laco(turtle.xcor(),turtle.ycor(),turtle.heading()+90,lado,cor)
if n % 2 == 0:
turtle.setheading(orientacao + inc)
turtle.forward(comp)
else:
turtle.setheading(orientacao - inc)
turtle.forward(comp)
</pre>
E chega… ou talvez não! <br><br>
Para terminar, e embora não fosse necessário, um pequeno programa para desenhar apenas uma cauda ondulante:
<pre name=‘code’ class=‘brush:python’>
def cauda(n,posx,posy,comp,orienta_1,orienta_2,cor):
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
turtle.pencolor(cor)
for i in range(n):
if i % 2 == 0 :
seg(turtle.xcor(),turtle.ycor(),comp, orienta_1,cor)
else:
seg(turtle.xcor(),turtle.ycor(),comp, orienta_2,cor)
</pre>
Note-se como controlamos a orientação dos segmentos.
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-35563000440977364542017-09-30T18:58:00.000+01:002017-09-30T18:58:13.044+01:00Zen ou a Arte de Programar<i>
<b>(brincadeiras de um sábado de manhã)</b></i><br><br><br>
Suponham que lhe pedem um programa capaz de desenhar o símbolo do <b>Yin-Yang</b>:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBLFYCDJlbb521H8Cvk0eaG2kDiex_NeBEOJ5K4LLGnJgvewL-UWb5Yr2M3Ur6V1hnr245M5QdaLkkTZWwLMU_h6V3f6eW_fbm1sCAf86ahnXQgW-2YSOCvDL5Uj1MSDhCJmWh9NuxDNSK/s1600/zen_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBLFYCDJlbb521H8Cvk0eaG2kDiex_NeBEOJ5K4LLGnJgvewL-UWb5Yr2M3Ur6V1hnr245M5QdaLkkTZWwLMU_h6V3f6eW_fbm1sCAf86ahnXQgW-2YSOCvDL5Uj1MSDhCJmWh9NuxDNSK/s320/zen_4.png" width="305" height="320" data-original-width="284" data-original-height="298" /></a></div>
Por onde começar?? Olhando para a figura verificamos que é composta à base de circunferências e semi-circunferências a duas cores (preto e branco). Usando um princípio já nosso conhecido, tentemos <b>simplificar a questão</b>, esquecendo os círculos interiores e as cores:
<pre class=‘brush:python’ name=‘code’>
def zen1(raio):
turtle.setheading(90)
turtle.circle(raio)
turtle.circle(raio/2,-180)
turtle.setheading(90)
turtle.circle(raio/2,180)
</pre>
Executando o código é criado o seguinte:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2Ft54PRc8-3on274iZPpuTEywQ5KqVVdCdj29EWkeHvg9Qew5dt1vcPALYbQtcYhHWN-sig7hG_8TljDtrW9ZoIHwzJhyphenhyphen42m7BaH4kHpMB_96OMiG4N8Oc-t1ruhT37x2WtUq1zFrDYY/s1600/zen_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha2Ft54PRc8-3on274iZPpuTEywQ5KqVVdCdj29EWkeHvg9Qew5dt1vcPALYbQtcYhHWN-sig7hG_8TljDtrW9ZoIHwzJhyphenhyphen42m7BaH4kHpMB_96OMiG4N8Oc-t1ruhT37x2WtUq1zFrDYY/s320/zen_1.png" width="320" height="286" data-original-width="323" data-original-height="289" /></a></div>
Ao fazermos assim ficamos com o problema de colocar as cores… Com um pouco de reflexão chegamos a uma solução, baseada na ideia de separar a circunferência exterior em duas semi-circunferências. O cuidado a ter é com as <b>orientações </b>a cada momento da tartaruga. Aqui basta não esquecer o principio de que <b>a tartaruga quando se movimenta vê sempre o centro à sua esquerda</b>!
<pre class=‘brush:python’ name=‘code’>
def zen11(raio):
turtle.setheading(90)
turtle.circle(raio,180)
turtle.fillcolor('black')
turtle.begin_fill()
turtle.circle(raio/2,-180)
turtle.setheading(270)
turtle.circle(raio/2,180)
turtle.circle(raio,-180)
turtle.end_fill()
</pre>
ao executar o programa obtemos:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoR1zAsx5P_tRD_T8-2vUGS5ThhHMm0DTpl9x8d3kn2y3Ev56A0Eaa6tozX28JXqJYw9-UwbU1COtZudcsPWb_95xSi6VSjclAeId6l0d8tk2bMXHSiSI2JRg0dHqd6aGWBSxXVuCnq816/s1600/zen_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoR1zAsx5P_tRD_T8-2vUGS5ThhHMm0DTpl9x8d3kn2y3Ev56A0Eaa6tozX28JXqJYw9-UwbU1COtZudcsPWb_95xSi6VSjclAeId6l0d8tk2bMXHSiSI2JRg0dHqd6aGWBSxXVuCnq816/s320/zen_5.png" width="308" height="320" data-original-width="267" data-original-height="277" /></a></div>
Agora é “só” colocar os círculos pequenos. Uma vez mais basta saber definir os centros:
<pre class=‘brush:python’ name=‘code’>
def zen12(raio):
turtle.setheading(90)
turtle.circle(raio,180)
turtle.fillcolor('black')
turtle.begin_fill()
turtle.circle(raio/2,-180)
turtle.setheading(270)
turtle.circle(raio/2,180)
turtle.circle(raio,-180)
turtle.end_fill()
# círculos pequenos
# esquerdo
turtle.setx(turtle.xcor() + raio/2 -raio/10)
turtle.fillcolor('white')
turtle.begin_fill()
turtle.circle(raio/10)
turtle.end_fill()
# direito
turtle.penup()
turtle.setx(turtle.xcor()+ raio)
turtle.pendown()
turtle.fillcolor('black')
turtle.begin_fill()
turtle.circle(raio/10)
turtle.end_fill()
turtle.hideturtle()
</pre>
Et voilá! <br><br>
Mas será que é a única forma de resolver o problema? Será que ficamos satisfeitos??? Olhando para a solução acima o que podemos dizer? Bem, que há muita coisa “fixa”: a posição, a orientação, as cores. Embora estas duas últimas façam parte da definição clássica da imagem, podemos procurar libertar-mo-nos e permitir uma versão mais geral. Por outro lado, e talvez mais importante, o modo como construímos a solução usa componentes de baixo nível e não olha para a imagem como sendo composta por duas que se distinguem … pela posição, orientação e cores!!! Vamos tentar então uma segunda abordagem.
<pre class=‘brush:python’ name=‘code’>
def zen(raio,x_cor,y_cor,orient, cor_1,cor_2):
"""
Desenha uma componente.
"""
# corpo principal
vai_para_or(x_cor,y_cor,orient)
turtle.fillcolor(cor_1)
turtle.begin_fill()
turtle.circle(raio,-180)
#vai_para_or(x_cor,y_cor,orient)
turtle.circle(raio/2,-180)
turtle.setheading(turtle.heading()+180)
turtle.circle(raio/2,180)
turtle.end_fill()
# restaura
vai_para_or(x_cor,y_cor,orient+90)
# círculo pequeno
turtle.penup()
turtle.forward(3*raio/2-raio/10)
turtle.pendown()
turtle.fillcolor(cor_2)
turtle.setheading(orient)
turtle.begin_fill()
turtle.circle(raio/10)
turtle.end_fill()
turtle.hideturtle()
</pre>
O código apresentado torna mais claro a importância da orientação em cada momento do desenho. O raio do círculo pequeno foi decidido que tenha um valor 10 vezes mais pequeno que o raio da semi-circunferência maior. O resultado quando escolhemos as cores vermelho e azul:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN2C7RHtdckfrpq0wbBH7elpiXMCrMCKf2fORymwO-AIDBm6NqaqvqwP18-1AbAMZ2Gd0bwnStBwTQiIN3W2jA7JMjZlbjUBx1lLsGFwAUmcmWVeO1oknVxfGkwbfQPhTDN4yqZrswbp_u/s1600/zen_12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN2C7RHtdckfrpq0wbBH7elpiXMCrMCKf2fORymwO-AIDBm6NqaqvqwP18-1AbAMZ2Gd0bwnStBwTQiIN3W2jA7JMjZlbjUBx1lLsGFwAUmcmWVeO1oknVxfGkwbfQPhTDN4yqZrswbp_u/s320/zen_12.png" width="320" height="310" data-original-width="285" data-original-height="276" /></a></div>
Agora podemos apresentar o nosso programa para o Yin-Yang:
<pre class=‘brush:python’ name=‘code’>
def yin_yang(raio,posx,posy):
zen(raio,posx,posy,90,'black','white')
zen(raio,posx - 2*raio,posy,270, ‘white','black')
</pre>
Com esta solução podemos então … brincar:
<pre class=‘brush:python’ name=‘code’>
def zen_rose(raio,posx,posy,orientacao,cor_1, cor_2, num):
for i in range(num):
zen(raio,posx,posy,orientacao,cor_1,cor_2)
orientacao = orientacao + 360/num
</pre>
Executando vem:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSJNeC5bhHyvPiVZSQu2DdP2UT085oogavqshQm7z2ssYb3PnJb4y-02NfyV1I1NS-scwCo1ZECqbt_lb7BF2pDIo-_WjXIhpAsRodIbjYImY90JWxoikzXpa_Pltt4iwhNQUKMyPaBkil/s1600/zen_8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSJNeC5bhHyvPiVZSQu2DdP2UT085oogavqshQm7z2ssYb3PnJb4y-02NfyV1I1NS-scwCo1ZECqbt_lb7BF2pDIo-_WjXIhpAsRodIbjYImY90JWxoikzXpa_Pltt4iwhNQUKMyPaBkil/s320/zen_8.png" width="320" height="301" data-original-width="727" data-original-height="684" /></a></div>
Mas podemos variar um pouco:
<pre class=‘brush:python’ name=‘code’>
def zen_rose_2(raio,posx,posy,orientacao,cor_1, cor_2, num):
for i in range(num):
if i % 2 == 0:
zen(raio,posx,posy,orientacao,cor_1,cor_2)
else:
zen(raio,posx,posy,orientacao,cor_2,cor_1)
orientacao = orientacao + 360/num
</pre>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpgQQugg4oZCr6E6R31W9uXvCp74DEz8FUgQ99NI6o0PX7wW8rd2x6D46Z1HSInvuFwUAK3ARK-yR4gEDgIXhyA-mTu-arkXqVrB13bNuWg7WLgW9nrg94ICaU7V75rPv4QUZkeDShD2AS/s1600/zen_9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpgQQugg4oZCr6E6R31W9uXvCp74DEz8FUgQ99NI6o0PX7wW8rd2x6D46Z1HSInvuFwUAK3ARK-yR4gEDgIXhyA-mTu-arkXqVrB13bNuWg7WLgW9nrg94ICaU7V75rPv4QUZkeDShD2AS/s320/zen_9.png" width="320" height="288" data-original-width="701" data-original-height="631" /></a></div>
Ou criar objectos mais coloridos:
<pre class=‘brush:python’ name=‘code’>
def zen_rose_tutti(raio,posx,posy,orientacao, num):
turtle.colormode(255)
for i in range(num):
cor_1 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
cor_2 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
zen(raio,posx,posy,orientacao,cor_1,cor_2)
orientacao = orientacao + 360/num
</pre>
E agora:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZOpFaQqVIXt3ic2YdY38aRQ9VP5aKDYP4zrEN0wSm35teBU6fAcmsk2EpUbIy74zvDkk42OX6ul9Y2mAXYf2o6XiiEC0UYZxxZWba7NIKhYoz7QrJn56AYLzpazkBQWcE27kP4RJbZLSm/s1600/zen_11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZOpFaQqVIXt3ic2YdY38aRQ9VP5aKDYP4zrEN0wSm35teBU6fAcmsk2EpUbIy74zvDkk42OX6ul9Y2mAXYf2o6XiiEC0UYZxxZWba7NIKhYoz7QrJn56AYLzpazkBQWcE27kP4RJbZLSm/s320/zen_11.png" width="320" height="285" data-original-width="726" data-original-height="647" /></a></div>
E como alguém disse: o céu é o limite! Invente!!!
<pre class=‘brush:python’ name=‘code’>
def zen_voa(raio,posx,posy,orientacao, num):
turtle.speed(10)
turtle.colormode(255)
for i in range(1,num+1):
cor_1 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
cor_2 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
zen(raio,posx,posy,orientacao,cor_1,cor_2)
#orientacao = orientacao + 360/num
posx = posx + i * raio/5
posy = posy + i * raio/5
</pre>
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-29257432199971869092017-09-30T10:45:00.000+01:002017-09-30T10:45:23.816+01:00Formas, Cores e outras coisas....O módulo <b>turtle</b> da linguagem <b>Python</b> tem um conjunto vasto de operações que podem ser exceptuadas sobre objectos do tipo <i>turtle</i> e sobre um mundo 2D. Durante as aulas vimos alguns exemplos de comandos básicos e o que com eles podíamos fazer. Por exemplo, para desenhar um quadrado:
<pre class=‘brush:python’ name = code’>
def quadrado(lado):
for i in range (4):
turtle.forward(lado)
turtle.right(90)
</pre>
Usando esta definição como componente podemos brincar um pouco. Por exemplo, desenhar um certo número de vezes um quadrado, mudando apenas a orientação:
<pre class=‘brush:python’ name = code’>
def tarta_ernesto(posx,posy):
# tartaruga ernesto
turtle.shape("turtle")
turtle.color("yellow")
turtle.speed(10)
turtle.pensize(2)
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
# desenha
for i in range(36):
quadrado(150)
turtle.right(10)
turtle.hideturtle()
</pre>
Como se pode ver existem comandos para:<br><br>
- definir a forma da tartaruga<br><br>
definir a cor<br><br>
definir a velocidade da tartaruga<br><br>
definir a espessura do rasto<br><br>
O resultado é ilustrado na figura.
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMiKgLipbIsSt-7FEFsnT_MG8bZ-BeQg_s7y4atdRHkibcMhYHD4TrrT5L4ndrtg6yQrIdqTDC6q42qrz-b3QLiODAOBKD5z3-eaRkAeusm7n9wyveYP0mdfXuQap_1ho_b7HoEKGiwk2v/s1600/star.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMiKgLipbIsSt-7FEFsnT_MG8bZ-BeQg_s7y4atdRHkibcMhYHD4TrrT5L4ndrtg6yQrIdqTDC6q42qrz-b3QLiODAOBKD5z3-eaRkAeusm7n9wyveYP0mdfXuQap_1ho_b7HoEKGiwk2v/s320/star.png" width="320" height="313" data-original-width="484" data-original-height="474" /></a></div>
Mudando o ciclo no interior do programa podemos conseguir outro tipo de formas:
<pre class=‘brush:python’ name = code’>
def tarta_costa(posx,posy):
#tartaruga costa
turtle.shape("turtle")
turtle.color("blue")
turtle.speed(10)
turtle.pensize(2)
# posiciona
turtle.penup()
turtle.goto(posx,posy)
turtle.pendown()
# desenha
for i in range(400):
turtle.forward(i)
turtle.right(91)
turtle.hideturtle()
</pre>
Tal como está escrito, a única “coisa” que controlamos e podemos variar é a posição inicial. Visualmente:
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUxmD2tMz1VQUe2PqiIbiSoFYIBc7_n_b9N4d1wFcA7QLva9b4OoG1CP8tL8sHLUxBVjOcSp7gLxmUAn7v-y4Y-k-fS4EBuGm-JOsr6D2AayDeXIrKeJJsYDC-A0CrTqgsYoCtSxHj-Wfi/s1600/spiral.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUxmD2tMz1VQUe2PqiIbiSoFYIBc7_n_b9N4d1wFcA7QLva9b4OoG1CP8tL8sHLUxBVjOcSp7gLxmUAn7v-y4Y-k-fS4EBuGm-JOsr6D2AayDeXIrKeJJsYDC-A0CrTqgsYoCtSxHj-Wfi/s320/spiral.png" width="320" height="286" data-original-width="692" data-original-height="619" /></a></div>
Podemos usar a mesma ideia de base (uma forma básicas que é replicada….) para criar diferentes formas coloridas. Experimente você.
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-40070319276259575242017-09-27T19:08:00.000+01:002017-09-27T19:08:48.675+01:00Na aula foi pedido que escrevessem um programa que desenhasse <b>espirais</b>. Ficou claro, após alguma discussão, que o desenho iria depender de dois factores inter-relacionados: o tamanho do traço e o ângulo de viragem. Também é importante definir o número de “voltas” da espiral, claro, mas isso não causa problema de maior.<br><br>
Daqui resulta um programa simples:
<pre class=‘brush:python’ name=‘code’ >
import turtle
def spiro(tam,k,ang,size):
for i in range(size):
turtle.forward(tam + k*i)
turtle.right(ang)
turtle.hideturtle()
</pre>
Não é preciso pensarmos muito para perceber que os traços que vamos usar terão que ter o seu tamanho incrementado. Porquê? Bem, porque a não ser assim, em função do ângulo de viragem, em vez de uma espiral teremos repetições da mesma volta. E o ângulo? Se fôr muito pequeno vamos ter uma grande amplitude e dificuldade em girar 360 graus. Se fôr muito grande rapidamente os traços se intersectam. Mas o melhor é experimentar com valores concretos.
Experimente tentar obter espirais com o aspecto dos das figuras. E muitas mais. Divirta-se!<br>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge6ewUBKwCmaCypfyZJjMKaC4GKt3dUYI3LFSLuWbknfw6xbHaemnwcnlXiMNib1acwtr-dYIQfCcw8bh4bsQRw2yyc1aBAg-0Ey4v9m5BGv6RwImmUG1MwbN1EMv6SOC60B_NqOC01Ewl/s1600/spiro_quad.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge6ewUBKwCmaCypfyZJjMKaC4GKt3dUYI3LFSLuWbknfw6xbHaemnwcnlXiMNib1acwtr-dYIQfCcw8bh4bsQRw2yyc1aBAg-0Ey4v9m5BGv6RwImmUG1MwbN1EMv6SOC60B_NqOC01Ewl/s320/spiro_quad.png" width="310" height="320" data-original-width="216" data-original-height="223" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOY3tlxW26wXeQxzzngak5ltXBhz2MpxfiJ0-mnu2ht-3A7sX1IpjQ4jIWMmhnPXifKGqAOmokLvu7ULK8TX6JzmfK7sVHElz0iQINqTLXTyVVOb7KBvuWnavRI7na4r-G1aip-O9fS-6i/s1600/spiro_hexa2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOY3tlxW26wXeQxzzngak5ltXBhz2MpxfiJ0-mnu2ht-3A7sX1IpjQ4jIWMmhnPXifKGqAOmokLvu7ULK8TX6JzmfK7sVHElz0iQINqTLXTyVVOb7KBvuWnavRI7na4r-G1aip-O9fS-6i/s320/spiro_hexa2.png" width="320" height="283" data-original-width="245" data-original-height="217" /></a></div>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-69136353409564515772017-09-22T09:25:00.001+01:002017-09-22T09:25:43.512+01:00Mais uma vez...Vamos recomeçar mais uma edição da cadeira de IPRP. Como no passado vou procurar deixar aqui elementos que podem ajudar à melhor compreensão da matéria e consequente melhoria do vosso desempenho.<br><br>
<b>Estejam atentos!</b>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-80513175762365335472017-02-19T09:59:00.002+00:002017-02-19T19:12:04.159+00:00EXAME RECURSO - P1Dado o programa
<pre name='code' class='brush:python'>
def xpto(lista):
# ordena lista de modo decrescente
for i in range(len(lista)):
m = max(lista[i:])
ind = lista.index(m)
lista[i],lista[ind] = lista[ind], lista[i]
return lista
</pre>
pretende-se saber o que faz, como o faz e se, eventualmente, tem algum erro e como pode ser corrigido.<br><br>
O programa recebe como entrada uma lista de números e devolve essa mesma lista ordenada de modo decrescente. Funciona percorrendo a lista da esquerda para a direita e na etapa <b>i</b> determinar a posição do maior elemento da sub-lista desde a posição <b>i<b></b></b> até ao final. De seguida esse elemento troca a sua posição com o elemento na posição <b>i</b>. Existe uma situação em que este algoritmo não funciona, quando existem elementos repetidos. Para corrigir, basta usar a instrução
<pre name='code' class='brush:python'>
ind = lista.index(m,i)
</pre>
no lugar de
<pre name='code' class='brush:python'>
ind = lista.index(m)
</pre>Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-40847374366874644422017-01-08T10:03:00.002+00:002017-01-08T10:03:38.344+00:00Teste 3 - TP2<b>Problema 2</b><br><br>
Saber se temos prémio no <b>euromilhões</b> e o seu valor não é tarefa difícil. Na solução abaixo admitimos que vamos ter a chave correcta e a nossa chave representadas por uma lista de dois elementos. O primeiro, é uma lista de cinco números e, o segundo, uma lista de dois números (as estrelas). Por outro lado o valor dos prémios está armazenado num dicionário em que a chave é um tuplo <b>(n1, n2)</b> que traduz quantos números e estrelas acertámos e o valor é o prémio.<br>
<pre class=‘brush:python’ name=‘code’>
def premio_euro(dicio,chave_certa, minha_chave):
# as chaves estão na forma [[5 números],[2 estrelas]]
# verifica números
num_certos = chave_certa[0]
num_meus = minha_chave[0]
conta_n = 0
for num in num_meus:
if num in num_certos:
conta_n += 1
# verifica estrelas
est_certas = chave_certa[1]
est_minhas = minha_chave[1]
conta_e = 0
for est in est_minhas:
if est in est_certas:
conta_e += 1
# calcula prémio
return dicio.get((conta_n,conta_e),0)
</pre>
<b>Problema 3</b><br><br>
Pretende-se <b>normalizar</b> os números guardados num ficheiro. Cada linha do ficheiro contém um nome e os números. A normalização é feita por linha, subtraindo a cada número a média dos valores dos números da linha e dividindo o resultado pelo respectivo desvio padrão.<br>
<pre class=‘brush:python’ name=‘code’>
import statistics
def normaliza_fich(fich_entrada, fich_saida):
# abre ficheiros
f_in = open(fich_entrada,'r',encoding='utf8')
f_out = open(fich_saida,'w',encoding='utf8')
# lê e normaliza por linha
for linha in f_in:
# recolhe nome e números
linha = linha.strip().split()
nome = linha[0]
numeros = [int(num) for num in linha[1:]]
# normaliza números
num_normais = normaliza(numeros)
# escreve resultado
nova_linha = nome + ' '.join([str(num) for num in num_normais]) + ‘\n'
f_out.write(nova_linha)
# fecha ficheiros
f_in.close()
f_out.close()
def normaliza(numeros):
media = statistics.mean(numeros)
desvio_pad = statistics.stdev(numeros)
return [(num - media)/desvio_pad for num in numeros]
</pre>
A função auxiliar <b>normaliza</b> usa o módulo <b>statistics</b> para normalizar os números dados numa lista.
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-81310656615482203452017-01-07T20:12:00.002+00:002017-01-08T09:46:30.832+00:00Teste 3 - TP1<b>Problema 2</b><br><br>
Num dicionários a diferentes chaves podemos ter associado o mesmo valor. O problema de calcular qual o valor mais frequente pode ser resolvido de forma simples do seguinte modo:
<pre class='brush:python' name='code'>
def moda(dicio):
# inverte dicio
novo_dicio = {}
for c,v in dicio.items():
novo_dicio[v] = novo_dicio.get(v,[]) + [c]
# passa a lista ordenada
lista_items = list(novo_dicio.items())
mais_freq = (0,[])
for val,lst in lista_items:
if len(lst) > len(mais_freq[1]):
mais_freq = (val,lst)
# devolve o mais valor mais frequente
return mais_freq
</pre>
Como os comentários indicam, a estratégia de solução passa por <b>inverter</b> o dicionário, <b>converter</b> para uma lista e depois calcular o elemento, i.e. o par (valor, lista das chaves com esse valor), mais frequente. Para os pythónicos apresentamos outra solução que recorre a <b>funções anónimas</b>:
<pre class='brush:python' name='code'>
def moda_b(dicio):
# inverte dicio
novo_dicio = {}
for c,v in dicio.items():
novo_dicio[v] = novo_dicio.get(v,[]) + [c]
# passa a lista ordenada
lista_items = list(novo_dicio.items())
lista_items.sort(key=lambda x: len(x[1]),reverse=True)
# devolve o mais valor mais frequente
return lista_items[0]
</pre>
A função anónima (<b>lambda</b>) é usada para o ordenamento ser feito de acordo com o tamanho do elemento na posição 1.<br><br>
<b>Problema 3</b><br><br>
O problema envolvia um ficheiro em que cada linha é formado por um nome e números (>= 3). Pretende-se criar um novo ficheiro com o nome e a média dos números depois de retirar o menor e o maior.<br><br>
Uma solução mágica:
<pre class='brush:python' name='code'>
def fich_media(fich_entrada,fich_saida):
# modificar linha a linha
# abre ficheiros
f_in = open(fich_entrada,'r',encoding='utf8')
f_out = open(fich_saida,'w',encoding='utf8')
# trata por linha
for linha in f_in:
# escolhe numeros
linha = linha.strip().split()
nome = linha[0]
numeros = [ int(num) for num in linha[1:]]
numeros.sort()
# calcula média
media = sum(numeros[1:-1])/(len(numeros)-2)
# escreve nova linha
f_out.write(nome + str(media) + '\n')
# fecha ficheiros
f_in.close()
f_out.close()
</pre>
Dada a natureza do enunciado resolvemos natural tratar o problema <b>linha a linha</b>. Cada linha é partida e os seus números ordenados. Calculamos de seguida a média retirando o primeiro (o mais pequeno) e o último (o maior).
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com1tag:blogger.com,1999:blog-7850634310261594358.post-83750276599333894752016-11-20T11:04:00.002+00:002016-11-20T11:06:14.936+00:00Teste # 2 - Solução dos problemas (TP1 e TP2)
<b>Turma TP1</b><br><br>
<b>P2</b><br><br>
O problema 2 pedia para criar um programa que transformasse uma cadeia de caracteres numa outra na qual a primeira ocorrência de um dado caractere fosse replicada um numero de vezes na nova cadeia igual à posição onde ocorre na cheia original mais um. Era necessário manter a ordem relativa das ocorrências dos diferentes caracteres. Este exercício tinha uma solução simples baseada no padrão tantas vezes trabalhado de <b>ciclo - acumulador</b>.
<pre class='brush:python' name='code'>
def mul_car(cadeia):
cad = ''
for i in range(1,len(cadeia)+1):
elem = cadeia[i-1]
if elem not in cad:
cad += elem * i
return cad
</pre>
cad é o acumulador onde vamos acrescentando às soluções parciais as ocorrências do novo caractere. notar a existência da condicional que serve para filtrar os caracteres individuais já utilizados.<br><br>
Quem conhece Python de um modo mais profundo podia propor uma versão alternativa baseada em listas por compreensão:
def mul_car2(cadeia):
return ''.join([cadeia[i] * (i+1) for i in range(len(cadeia)) if cadeia[i] not in cadeia[:i]])
Note-se o desaparecimento explicito do acumulador e o modo como se transforma a lista resultado numa cadeia de caracteres.<br><br>
<b>P3</b><br><br>
A sobreposição de duas imagens a preto e branco, representadas como listas de listas de uns e zeros não representa problema de maior. A sobreposição significa que basta que numa dada posição (um dado pixel) esteja a 1 (preto) o resultado na nova imagem deve também ser 1. Percorrendo naturalmente a imagem com dois ciclos for e alternado um a um chegamos ao resultado desejado.
<pre class='brush:python' name='code'>
def sobreposicao(img1, img2):
nova_img = []
for i in range(len(img1)):
nova_linha = []
for j in range(len(img1[0])):
nova_linha.append(img1[i][j] or img2[i][j])
nova_img.append(nova_linha)
return nova_img
</pre>
Também aqui podemos recorrer a listas por compreensão para obter um programa mais curto:
<pre class='brush:python' name='code'>
def sobreposicao2(img1,img2):
return [ [(img1[i][j] or img2[i][j]) for j in range(len(img1[0]))] for i in range(len(img1))]
</pre>
Notar a existência de dois ciclos e como o ciclo mais interior aparece primeiro.<br><br>
<b>Turma TP2</b><br><br>
<b>P2</b><br><br>
Anagrama é um conceito conhecido: palavras formadas por permutações das mesmas letras e em igual quantidade. O exemplo clássico em português é dado pelas palavras “roma” e “amor”. Uma solução ingénua para esta questão seria testar o igual comprimento das palavras e depois verificar se cada caractere de uma ocorre na outra.
<pre class='brush:python' name='code'>
def anagramas1(cad1,cad2):
"""versão errada!"""
if len(cad1) != len(cad2):
return False
for elem in cad1:
if elem not in cad2):
return False
return True
</pre>
Para verificar que está errada basta testar com as palavras ‘aab’ e ‘bba’. Em vez de falso vai dar verdadeiro. O problema está no facto de o numero de ocorrência de cada caractere em cada uma das palavras ter que ser o mesmo. Daí que uma solução simples seja:
<pre class='brush:python' name='code'>
def anagramas2(cad1,cad2):
if len(cad1) != len(cad2):
return False
for elem in cad1:
if cad1.count(elem) != cad2.count(elem):
return False
return True
</pre>
Claro que podemos pensar em alternativas. Por exemplo, transformar as cadeias em listas, ordená-las e verificar se resulta em duas listas … iguais:
<pre class='brush:python' name='code'>
def anagramas3(cad1,cad2):
list_cad1 = list(cad1)
list_cad2 = list(cad2)
list_cad1.sort()
list_cad2.sort
return list_cad1 == list_cad2
</pre>
Para os <i>pitónicos</i> puristas (peritos??) temos outra solução:
<pre class='brush:python' name='code'>
from collections import Counter
def anagramas4(cad1, cad2):
return Counter(cad1) == Counter(cad2)
</pre>
<b>Counter</b> é um typo que se pode definir como uma colecção não ordenada que implementada como um <b>dicionário</b> em que as chaves são os elementos e o valor o numero de ocorrências da chave. O conceito de dicionário será dado na próxima aula!<br><br>
Quem não souber da existência de Counter pode implementar a sua solução:
<pre class='brush:python' name='code'>
def conta_elems(seq):
conta = {}
for elem in seq:
counts[elem] = counts.get(elem, 0) + 1
return conta
def anagramas4b(cad1, cad2):
return conta_elems(cad1) == conta_elems(cad2)
</pre>
Parece que já temos mulitas alternativas e que dificilmente arranjaremos outra substancialmente diferente. Ou será que não??? Olhemos o código abaixo:
<pre class='brush:python' name='code'>
def anagramas5(cad1, cad2):
return [False,True][sum([ord(x) for x in cad1]) == sum([ord(x) for x in cad2])]
</pre>
Experimente e … surpresa! Parece que funciona. Mas como? Como se pode ver usamos <b>listas por compreensão</b> para transformar cada lista na lista dos seus códigos numéricos. Esses códigos são somados e verificamos se são ou não iguais. O resultado por isso ou é True ou é False. Como disse nas aulas, True é representado por 1 e False por zero. Então o que temos no final é a forma [False,True][1] ou [False,True][0], isto é estamos a obter por indexação o elemento da lista [False,True] ou na posição zero (False) ou na posição um (True). Engenhoso, mas por ventura não muito claro e dependente do modo como estão implementados os booleanos.<br><br>
<b>P3</b><br><br>
A <b>intersecção</b> de duas imagens é semelhante à sobreposição. A diferença agora é que devemos ter um apenas nas situações em que as duas imagens estejam, na mesma posição, iguais a um. Assim uma solução simples será:
<pre class='brush:python' name='code'>
def interseccao(img1, img2):
nova_img = []
for i in range(len(img1)):
nova_linha = []
for j in range(len(img1[0])):
nova_linha.append(img1[i][j] and img2[i][j])
nova_img.append(nova_linha)
return nova_img
</pre>
Também aqui podíamos recorrer a uma solução com listas por compreensão. Ao leitor o cuidado de o fazer.
Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0tag:blogger.com,1999:blog-7850634310261594358.post-8574471512485461722016-11-13T10:23:00.002+00:002016-11-13T10:23:30.339+00:00Um problema, várias soluçõesSempre que estamos diante de um problema novo a melhor maneira de o resolver é tentar identificar um modelo de solução por <b>analogia</b> com problemas antigos já resolvidos. Para além disso, quando os problemas têm uma certa complexidade, pois necessitamos fazer muitas coisas e sujeitos a diferentes restrições, nada melhor do que <b>dividir o problema em sub-problemas mais simples</b> e/ou tentar <b>resolver primeiro uma versão mais simples</b> que depois completamos. Isto são princípios que temos vindo a explorar ao longo das aulas. Também é um facto que conhecendo melhor a linguagem de programação podemos encontrar variantes para a nossa solução inicial, eventualmente mais eficientes. Vamos ver este último aspecto com exemplos simples das aulas.<br><br>
<b>Contar quantos elementos de uma lista são menores do que um elemento de referência.</b><br><br>
A solução trivial passa por percorrer (recorrendo a um ciclo) a lista e ir contando sempre que aparecer um elemento menor (uso de um acumulador. Mas mesmo esta situação pode ser feita de diferentes maneiras: percorrer a lista por índice ou percorrer por conteúdo:
<pre class='brush:python name='code'>
def conta_menores1(num,lista_num):
acum = 0
for i in range(len(lista_num)):
if lista_num[i] < num:
acum += 1
return acum
def conta_menores2(num,lista_num):
acum = 0
for i in lista_num:
if i < num:
acum += 1
return acum
</pre>
A segunda versão é a preferível do ponto de vista da eficiência, sendo que, na nossa opinião, é mais clara. Quem conhecer o conceito de listas por compreensão pode sugerir outra solução:
<pre class='brush:python name='code'>
def conta_menores3(num, lista_num):
return sum([ 1 for i in lista_num if i < num])
</pre>
Será que podemos arranjar ainda outra solução. Na aula um aluno sugeriu uma alternativa muito interessante:
<pre class='brush:python name='code'>
def conta_menores4(num, lista_num):
lista_num.sort()
return lista_num.index(num)
</pre>
Nesta solução ordenamos a lista e depois calculamos a posição do número da lista que vai ser igual ao número de elementos menores. Mas esta solução tem um problema: só funciona se o número de referência estiver na lista. Consegue perceber porquê?? Tratemos agora de uma variante deste problema.<br><br>
<b>
Listagem dos menores</b><br><br>
Podemos ter soluções semelhantes às anteriores. O que muda é a natureza do acumulador (agora terá que ser um contentor, tuplo ou lista.
<pre class='brush:python name='code'>
def lista_menores1(num,lista_num):
acum = []
for i in range(len(lista_num)):
if lista_num[i] < num:
acum += [lista_num[i]]
return acum
def lista_menores2(num,lista_num):
acum = []
for i in lista_num:
if i < num:
acum += [i]
return acum
</pre>
Aqui podemos variar a forma de juntar o elemento à lista, recorrendo ao método append em substituição da operação de concatenação:
<pre class='brush:python name='code'>
def lista_menores3(num,lista_num):
acum = []
for i in range(len(lista_num)):
if lista_num[i] < num:
acum.append(lista_num[i])
return acum
def lista_menores4(num,lista_num):
acum = []
for i in lista_num:
if i < num:
acum.append(i)
return acum
</pre>
E uma vez mais a solução com listas por compreensão (a nossa preferida):
<pre class='brush:python name='code'>
def lista_menores5(num, lista_num):
return [i for i in lista_num if i < num]
</pre>
Será que a sugestão do nosso aluno também se aplica aqui (no pressuposto de que o elemento está na lista)? Claro:
<pre class='brush:python name='code'>
def lista_menores4(num,lista_num):
lista_num.sort()
return lista_num[:lista_num.index(num)]
</pre>
<br><br>
<b>Soma cumulativa</b><br><br>
Agora a questão é a de dada uma lista construir uma nova lista em que na posição <b>i</b> vamos ter a soma dos elementos da lista original entre as posições inicial e <b>i</b> (inclusivé). A solução mais simples baseia-se no recurso ao <b>padrão ciclo-acumulador</b> que contem no interior do ciclo outro padrão ciclo-acumulador. Este último é usado para o cálculo da soma cumulativa.
<pre class='brush:python name='code'>
def soma_cumulativa(lista):
acum = []
for i in range(len(lista)):
# soma de 0 a i
soma = 0
for j in range(i+1):
soma += lista[j]
# junta soma ao acumulador
acum.append(soma)
return acum
</pre>
Conhecendo a existência da função sum podemos simplificar a nossa primeira solução:
<pre class='brush:python name='code'>
def soma_cumulativa2(lista):
acum = []
for i in range(len(lista)):
# soma de 0 a i
soma = sum(lista[:i+1])
# junta soma ao acumulador
acum.append(soma)
return acum
</pre>
E, também aqui, o recurso a listas por compreensão da origem a um programa mais curto:
<pre class='brush:python name='code'>
def soma_cumulativa3(lista):
return [sum(lista[:i+1]) for i in range(len(lista))]
</pre>
Será que podemos fazer melhor? Podemos. Uma pequena reflexão sobre o problema mostra que existe uma relação simples entre duas somas cumulativas consecutivas: basta somar à soma anterior (de <b>0</b> a <b>i</b>) o valor do elemento na posição <b>(i+1)</b>:
<pre class='brush:python name='code'>
def soma_cumulativa4(lista):
acum = [lista[0]]
for i in lista[1:]:
acum.append(acum[-1]+i)
return acum
</pre>
E pronto. Esperamos que tenham ficado com uma ideia de que em programação mesmo os problemas mais simples podem ter várias alternativas.Ernesto Costahttp://www.blogger.com/profile/11136340077636547515noreply@blogger.com0