quarta-feira, 16 de setembro de 2015

Cá estamos de novo

Mais um ano que começa.
A finalidade deste blogue não se alterou: servir de apoio à cadeira de IPRP, da licenciatura em Engenharia Informática da Universidade de Coimbra.

sexta-feira, 30 de janeiro de 2015

Exame Recurso - Pergunta 1b

As respostas dadas pela generalidade dos(as) alunos(as) denota uma incompreensão de aspectos básicos e fundamentais de Python. Em Python tudo são objectos, e os objectos têm atributos. O nome é um dos atributos, e é através do nome que chegamos a outro valor, o valor. Assim, temos por exemplo:
>>> x = 5
>>> x
5
>>> a = ‘xpto’
>>> a
xpto
Neste exemplo, criámos objectos (5, ‘xpto’) e demos-lhes um nome (x, a).

Mas também se fizermos:
>>> def acme(n):
…  return 2*n
…
>>>
Criamos também um objecto, agora do tipo função, e estamos a dar-lhe o nome acme. Se agora perguntarmos pelo valor do objecto de nome acme o que recebemos de volta é o descritor da função:
>>> acme
<function acme at 0x102778488>
Então, no caso da pergunta do exame, quando fazemos:
a = funcao
Estamos a dizer que a é um outro nome para o objecto de tipo função, funcao. Logo, ao fazermos:
>>> print(a)
o que vai aparecer é a impressão do valor associado ao objecto de nome a, que é o mesmo do associado a funcao:
>>> def funcao():
…    print('128')
…
>>> a = funcao
>>> a
<function funcao at 0x102778510>
Notar que pode agora usar o nome a em vez de funcao e obter o mesmo resultado:
>>> funcao()
128
>>> a()
128

Exame Recurso - Pergunta 1a

Nesta pergunta estavam envolvidos vários conceitos: alcance das variáveis (local, global), passagem de parâmetros (real, formal) e mutabilidade. Na primeira situação, na chamada da função f1, a lista l (variável global, mutável) é usada como parâmetro real. Deste modo, no início da execução da função, l e lista são nomes de objectos que têm a mesma identidade. No entanto, na primeira instrução de f1 a atribuição lista = [1,2,3] faz com que os nomes ficam associados a objectos diferentes. Deste modo, todas as alterações a lista não afectam l, pelo que na linha 12 vai aparecer o valor (inalterado) de l = [1,2].

Na segunda situação, ao contrário da anterior, não é desfeita a ligação dos nomes l e lista ao mesmo objecto. Tratando-se de um objecto mutável, todas alterações feitas ao parâmetro formal serão reflectidas no parâmetro real, pelo que ao acrescentarmos 3 à lista usando o método append, essa alteração vai também afectar l. Logo, na linha 17, vai aparecer [1,2,3].

quinta-feira, 8 de janeiro de 2015

Teste Final - Pergunta 4

Esta pergunta envolvia manipular um ficheiro em que cada linha é formada por um nome seguido de um ou mais números inteiros. O objectivo é criar um novo ficheiro, deixando o antigo inalterado, adicionando no final de cada linha a soma dos números dessa linha. Valorizava-se uma solução em que as linhas no ficheiro eram reordenadas por ordem crescente das somas.

Solução:

A solução apresentada inclui o ordenamento das linhas. Para isso usamos o método itemgetter do módulo operator. Começamos por percorrer o ficheiro linha a linha, calculando a soma dos números e guardando o resultado numa lista. É feito o ordenamento da lista de seguida. Na terceira parte, criamos o novo ficheiro e guardamos a informação.
from operator import itemgetter

def trata_ficheiro(ficheiro,novo_ficheiro):
    with open(ficheiro,'r') as f_in:
        novos_dados = list()
        for linha in f_in:
            lista_linha = linha[:-1].split()
            valor = sum([int(elem) for elem in lista_linha[1:]])
            lista_linha.append(valor)
            novos_dados.append(lista_linha)
    
    novos_dados.sort(key=itemgetter(-1))
    with open(novo_ficheiro,'w') as f_out:
        for linha in novos_dados:
            linha[-1] = str(linha[-1]) + '\n'
            cadeia = ' '.join(linha)
            f_out.write(cadeia)
            
    return novos_dados

Teste Final - Pergunta 3

Guardamos num dicionário as classificações que diversos filmes receberam dos críticos (um valor entre 1 e 5). Queremos manter o dicionário actualizado e saber qual o filme com melhor pontuação média. Só podem ser candidatos a melhor filme os que tiverem pelo menos 3 valores.

Solução:

A solução abaixo usa o setdefault para actualizar o dicionário das classificações. Deste modo fica resolvida a questão de incluir uma primeira classificação ou simplesmente actualizar. Actualizado o dicionário, ele é percorrido, item a item, para determinar o melhor, prevendo o caso de não ter as 3 classificações.
def melhor_filme(filmes, classific):
    # actualiza
    nome = classific[0]
    valores = classific[1:]
    filmes[nome] = filmes.setdefault(nome,[]) + valores
    # calcula melhor
    m_filme = ''
    m_pont = 0
    for nome,pontos in filmes.items():
        comp = len(pontos)
        if comp < 3:
            continue
        media = (sum(pontos)/comp)
        if media > m_pont:
            m_filme = nome
            m_pont = media
    return (m_filme, m_pont)

Teste Final - Pergunta 2

Simular um jogo em que se atiram dardos a um alvo. No caso do teste o problema era mais simples pois não se pretendia simular o jogo mas apenas desenhar o alvo e simular o lançamento de um dardo. E fazer isto usando o módulo turtle. E construindo o programa de forma modular.

Vamos então dividir a solução em duas partes: uma para desenhar o alvo, e outra para simular o lançamento do dardo. Por outro lado, vamos fazer com que o desenho do alvo seja feito à custa de uma mesma função, a que desenha um círculo de uma dada cor, numa dada posição e com o dado raio. Essa função chama-se circunf. A partir dela desenhar o alvo é fácil (função alvo). Estas última precisa saber apenas o centro do alvo e o valor dos raios. Os cuidados a ter são desenhar as circunferências da maior para a mais pequena (notar o ordenamento inverso da lista dos raios), e controlar a alternância das cores.

O lançamento do dardo recorre ao módulo random para geral a posição, e ao comando dot para marcar a localização do dardo. Neste último caso, não conhecendo o comando dot, podia socorrer-se uma vez mais ao desenho de uma circunferência…
import turtle

def circunf(posx, posy, raio, cor):
    # posiciona
    turtle.pu()
    turtle.goto(posx,posy-raio)
    turtle.pd()
    # define cor e desenha
    turtle.fillcolor(cor)
    turtle.begin_fill()
    turtle.circle(raio)
    turtle.end_fill()
    turtle.hideturtle()
    
def alvo(posx, posy,raios):
    """
    raios: lista dos raios dos discos
    """
    raios.sort(reverse=True)
    cor = 'black'
    for raio in raios:
        circunf(posx,posy,raio,cor)
        if cor == 'black':
            cor = 'white'
        else:
            cor = 'black'

            
def dardo():
    import random
    posx = random.randint(-100,100)
    posy = random.randint(-100,100)
    turtle.pu()
    turtle.goto(posx,posy)
    turtle.pd()
    turtle.dot(8,'red')
    turtle.hideturtle()


if __name__ == ‘__main__’:
    alvo(0,0,[20,40,60,80])
    dardo()
    turtle.exitonclick()
Como se pode ver pelo código o programa é facilmente generalizável. Alvo de vários tamanhos (directo) e cores (passando as cores para argumentos), por exemplo.

Teste Final - Pergunta 1a

Pedia-se para dizer o que eram e para que serviam as excepções e as asserções. Por outro lado, também se pretendia saber as suas semelhanças e as suas diferenças, e ainda a apresentação de um exemplo concreto de cada.

De um modo geral excepções e asserções são formas de controlar possíveis erros ou verificar condições num programa.

As excepções são instruções de controlo, enquanto as asserções são instruções simples. As excepções permitem que o programa continue a ser executado, mesmo na presença de um erro, pois prevê acções de remediarão. No caso das asserções isso não acontece, pois o programa termina no caso da asserção não se verificar. Assim estas últimas são sobretudo usadas para depurar os programas.

As excepções são activadas (raise) quando durante a execução se verifica um erro. Os erros podem ser de diversos tipos, podendo as excepções ser genéricas ou dirigidas a um ou mais tipos de erro. Um exemplo simples e clássico é prevenir erros nas operações:
def divisao_protegida(x,y):
 try:
  res = x/y
  return res
 except ZeroDivisionError:
  print(‘ERRO: divisão por zero’)
No caso das asserções elas permitem verificar condições para que um programa possa correr. Um exemplo, também clássico, permite evitar um ciclo infinito:
def factorial(x):
 assert (x >=0), ‘ERRO: número negativo’
 res = 1
 while x:
  res *= x
  x -= 1
 return res