sexta-feira, 22 de janeiro de 2010

Dúvidas ... e certezas!

Recebi de um colega vosso o seguinte email (apenas reproduzo as partes relevantes):

----------------------------------------------------------------------------------
Pretendo criar um programa que conte o número de ocorrências que cada
palavra tem num ficheiro. Parti do exercício do exame, uma vez que
tenho que retirar sinais de pontuação.

Tenho este código mas não consigo continuar.

def conta_palavras():
  a = open('texto.txt','r')
  tex = a.read().split()
  txt = []
  sinais = ['!',',','.',':']
  for palavra in tex:
      if palavra[-1] in sinais:
          palavra = palavra[:-1]
          txt.append(palavra)
      else:
          palavra = palavra
          txt.append(palavra)



------------------------------------------------------------------------------------

Olhando para o que está feito, e sabendo que a solução do exame foi colocada aqui neste blogue, fica-me a convicção que @ alun@ não sabe da sua existência. Esta situação de ignorância foi confirmada recentemente pela quantidade de alun@s que apareceram para ver o exame mas que desconheciam a existência do blogue, logo nem sequer tinham visto a respectiva solução. É um espanto! Curiosamente tenho tido feedback de pessoas de outros países que estão a seguir o que aqui tenho colocado. Sinal dos tempos!!

Olhando para o código, verifico que a parte para obter a lista de palavras está mais ao menos aceitável. Algumas notas:

a) O nome do ficheiro não é dado como parâmetro da função, o que é uma má prática de programação;
b) Deve-se procurar usar nossos que tenham algum significado. Se um programa tiver centenas de milhares de linhas de código, aparecer por lá um a diz pouco do que é e para que serve;
c) Há uma ineficiência no if: fazer palavra = palavra, serve para quê? Por outro lado, o código pode encolher:

for palavra in text:
if palavra[-1] in sinais:
palavra = palavra[:-1]
txt.append(palavra)

Agora vamos voltar ao problema. Uma coisa que devem procurar fazer é construir o programa passo a passo, deixando claro a vossa estratégia. Por exemplo, neste caso seria algo como:

def fich_dic(ficheiro):
""" Lê o conteúdo do ficheiro e constrói um dicionário
com chave uma palavra e valor o número de vezes que a palavra
ocorre no texto.
"""
# obter a lista de palavras
# filtra os sinais
# constrói dicionário
return # dicionário

Aparentemente avançámos pouco, mas pelo menos temos as tarefas bem identificadas e podemos passar a resolver as que sabemos resolver sem pensar muito:

# obter a lista de palavras
f_in = open(ficheiro, 'r')
lista_pal = f_in.read().split()

Que sinais vamos querer filtrar, e onde podem aparecer? Admitamos que podem aparecer no final, ou isolados, mas sempre só um símbolo. Outra decisão importante é saber se usamos a lista de palavras que já temos ou se fabricamos uma nova. Tendo decidido podemos passar a uma solução:

# filtra os sinais
especiais = ['\n','.',',','!','?']
lista_final = []
for palavra in lista_pal:
# símbolos especiais fora
if palavra in especiais:
continue
elif palavra[-1] in especiais:
palavra = palavra[:-1]
# acrescenta
lista_final.append(palavra)

Todas as decisões estão claras no código: símbolo isolado ou no final, nova lista. Reparar que esta solução torna trivial mudarmos os sinais a considerar. Imagine agora que pode ter outras marcas, como tabulações (‘\t’) ou outras. Acha que se resolve a questão, aumentando apenas a listas de sinais? É que este tem dois símbolos?! Experimente fazer:

# filtra os sinais
especiais = ['\n','.',',','!','?','\t']
lista_final = []
for palavra in lista_pal:
# símbolos especiais fora
if palavra in especiais:
continue
elif palavra[-1] in especiais:
palavra = palavra[:-1]
elif palavra[-2:] in especiais:
palavra = palavra[:-2]
# acrescenta
lista_final.append(palavra)

E verá a surpresa. Mesmo com o teste não funciona!. É que na realidade as marcas de controlo vão ter uma tradução ... bizarra: em vez de ‘\t’ aparece ’\\t’! Coisas. E se tiver marcas com mais símbolos? Pense um pouco e veja como resolvia a questão.

Mas voltemos à questão de querer manter a lista de palavras inicial. Uma solução simples seria:

# filtra os sinais
especiais = ['\n','.',',','!','?']
for indice,palavra in enumerate(lista_pal):
if palavra in especiais:
lista_pal.remove(palavra)
elif palavra[-1] in especiais:
lista_pal[indice] = palavra[:-1]

Note-se o recurso a enumerate!

Agora a construção do dicionário. Não há muito a dizer. A chave são as palavras, e os valores inteiros. Sendo os valores objectos imutáveis podemos usar o método get, e eis a solução para esta parte.

# Constrói dicionário
dic = {}
for palavra in lista_final:
dic[palavra] = dic.get(palavra,0) + 1
return dic

Como explicámos nas aulas e também neste blogue o método get é uma fprma elegante de resolver esta questão: Se a palavra ainda não estiver no dicionário, o método devolve o valor por defeito (0), que é somado a 1 , sendo o novo para colocado no dicionário. Se existir, devolve o valor que é na mesma somado e actualizado.

Vamos agora juntar todos os bocados:

def fich_dic(ficheiro):
""" Lê o conteúdo do ficheiro e constrói um dicionário
com chave uma palavra e valor o número de vezes que a palavra
ocorre no texto.
"""
# obter a lista de palavras
f_in = open(ficheiro, 'r')
lista_pal = f_in.read().split()
# filtra os sinais
especiais = ['\n','.',',','!','?']
lista_final = []
for palavra in lista_pal:
# símbolos especiais fora
if palavra in especiais:
continue
elif palavra[-1] in especiais:
palavra = palavra[:-1]
# acrescenta
lista_final.append(palavra)
# Constrói dicionário
dic = {}
for palavra in lista_final:
dic[palavra] = dic.get(palavra,0) + 1
return dic

Sem comentários:

Enviar um comentário