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:
txt = 'ernesto ui ui cuidado com os alunos’
o resultado deve ser:
{'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.
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
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:
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 
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.
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  
O leitor atento dirá que ainda se pode ter uma solução mais elegante usando uma atribuição aumentada: +=.
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 
No entanto, se correr este código verificará que não funciona. Ou melhor, corre mas apresenta o resultado errado:
{'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:
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 
E pronto. Espero que tenha entendido. Ah, já agora, ainda outra solução….
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

Sem comentários:

Enviar um comentário