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.
01.def conta_palavras():
02.  a = open('texto.txt','r')
03.  tex = a.read().split()
04.  txt = []
05.  sinais = ['!',',','.',':']
06.  for palavra in tex:
07.      if palavra[-1] in sinais:
08.          palavra = palavra[:-1]
09.          txt.append(palavra)
10.      else:
11.          palavra = palavra
12.          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:
1.for palavra in text:
2.   if palavra[-1] in sinais:
3.       palavra = palavra[:-1]
4.   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:
01.def fich_dic(ficheiro):
02.    """ Lê o conteúdo do ficheiro e constrói um dicionário
03.    com chave uma palavra e valor o número de vezes que a palavra
04.    ocorre no texto.
05.    """
06.    # obter a lista de palavras
07.    # filtra os sinais
08.    # constrói dicionário  
09.    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:
1.# obter a lista de palavras
2.f_in = open(ficheiro, 'r')
3.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:
01.# filtra os sinais
02.   especiais = ['\n','.',',','!','?']
03.   lista_final = []
04.   for palavra in lista_pal:
05.       # símbolos especiais fora
06.       if palavra in especiais:
07.           continue
08.       elif palavra[-1] in especiais:
09.           palavra = palavra[:-1]
10.       # acrescenta
11.       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:
01.# filtra os sinais
02.   especiais = ['\n','.',',','!','?','\t']
03.   lista_final = []
04.   for palavra in lista_pal:
05.       # símbolos especiais fora
06.       if palavra in especiais:
07.           continue
08.       elif palavra[-1] in especiais:
09.           palavra = palavra[:-1]
10.       elif palavra[-2:] in especiais:
11.           palavra = palavra[:-2]
12.       # acrescenta
13.       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:
1.# filtra os sinais
2.especiais = ['\n','.',',','!','?']
3.for indice,palavra in enumerate(lista_pal):
4.    if palavra in especiais:
5.        lista_pal.remove(palavra)
6.    elif palavra[-1] in especiais:
7.        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.
1.# Constrói dicionário  
2.    dic = {}
3.    for palavra in lista_final:
4.        dic[palavra] = dic.get(palavra,0) + 1
5.    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:
01.def fich_dic(ficheiro):
02.    """ Lê o conteúdo do ficheiro e constrói um dicionário
03.    com chave uma palavra e valor o número de vezes que a palavra
04.    ocorre no texto.
05.    """
06.    # obter a lista de palavras
07.    f_in = open(ficheiro, 'r')
08.    lista_pal = f_in.read().split()
09.    # filtra os sinais
10.    especiais = ['\n','.',',','!','?']
11.    lista_final = []
12.    for palavra in lista_pal:
13.        # símbolos especiais fora
14.        if palavra in especiais:
15.            continue
16.        elif palavra[-1] in especiais:
17.            palavra = palavra[:-1]
18.        # acrescenta
19.        lista_final.append(palavra)  
20.    # Constrói dicionário  
21.    dic = {}
22.    for palavra in lista_final:
23.        dic[palavra] = dic.get(palavra,0) + 1
24.    return dic

Sem comentários:

Enviar um comentário