sábado, 18 de novembro de 2017

Variações em torno de vogais

Nas aulas discutimos o problema de escrever um programa que leia um texto e indique para cada vogal a lista das suas ocorrências no texto. Trata-se de um problema em que, naturalmente, se opta por um dicionário para representar o resultado. Assim, para:
1.txt = 'ernesto ui ui cuidado com os alunos’
o resultado deve ser:
1.{'e': [0, 3], 'o': [6, 20, 23, 26, 33], 'u': [8, 11, 15, 31], 'i': [9, 12, 16], 'a': [18, 29]}
A solução evidente segue o padrão ciclo-acumulador. Aqui o acumulador é o dicionário que vai sendo actualizado à medida que percorremos o texto caractere a caractere.
01.def vogais_x(texto):
02.    dicio = dict()
03.    for i in range(len(texto)):
04.        if texto[i] == 'a':
05.            dicio['a'] = dicio.get('a',[]) + [i]
06.        elif texto[i] == 'e':
07.            dicio['e'] = dicio.get('e',[]) + [i]
08.        elif texto[i] == 'i':
09.            dicio['i'] = dicio.get('i',[]) + [i]
10.        elif texto[i] == 'o':
11.            dicio['o'] = dicio.get('o',[]) + [i]
12.        elif texto[i] == 'u':
13.            dicio['u'] = dicio.get('u',[]) + [i] 
14.        else:
15.            continue
16.    return dicio
Todos concordaremos que é uma solução feia. Todos aqueles ifs 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:
1.def vogais_y(texto):
2.    vogais ='aeiou'
3.    dicio = dict()
4.    for i,car in enumerate(texto):
5.        if texto[i] in vogais:
6.            dicio[car] = dicio.get(car,[]) + [i]
7.    return dicio
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 fromkeys. Como consequência deixamos de necessitar do uso do método get.
1.def vogais(texto):
2.    vogais ='aeiou'
3.    dicio = dict.fromkeys(vogais, [])
4.    for i,car in enumerate(texto):
5.        if car in vogais:
6.            dicio[car] = dicio[car] + [i]
7.    return dicio 
O leitor atento dirá que ainda se pode ter uma solução mais elegante usando uma atribuição aumentada: +=.
1.def vogais_z(texto):
2.    vogais ='aeiou'
3.    dicio = dict.fromkeys(vogais, [])
4.    for i,car in enumerate(texto):
5.        if car in vogais:
6.            dicio[car] += [i] # <———
7.    return dicio
No entanto, se correr este código verificará que não funciona. Ou melhor, corre mas apresenta o resultado errado:
1.{'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]}
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 fromkeys faz com que os valores iniciais sejam o mesmo objecto (partilha de memória) e a instrução += não constrói objectos novos. Uma solução consiste em alterar a construção do dicionário inicial, usando dicionários por compreensão:
1.def vogais_e(texto):
2.    vogais ='aeiou'
3.    dicio = {vog:[] for vog in 'aeiou'}
4.    for i,car in enumerate(texto):
5.        if car in vogais:
6.            dicio[car] += [i]
7.    return dicio
E pronto. Espero que tenha entendido. Ah, já agora, ainda outra solução….
1.def vogais_d(texto):
2.    vogais ='aeiou'
3.    dicio = {vog:[] for vog in 'aeiou'}
4.    for i,car in enumerate(texto):
5.        if car in vogais:
6.            dicio[car].append(i)
7.    return dicio

Sem comentários:

Enviar um comentário