domingo, 6 de dezembro de 2009

Estruturas de Dados e Abstracção

Regressemos à ideia de estado avançada no problema do Enforcado. Dissemos que o estado era o que andava a ser comunicado entre o programa e o utilizador. Dissemos também que a implementação seria feita com um dicionário. Existem algumas questões interessantes que se colocam. Uma primeira, prende-se com de saber como definir uma camada de abstracção entre a estrutura de dados estado e o resto do mundo. Uma segunda, é a de saber como reagir a uma modificação na implementação do estado, por exemplo, passando de dicionário para lista.


Para responder a estas questões vamos começar por definir uma estrutura de dados, a que chamaremos estado, e que, como referimos será implementada como um dicionário.
01.# estado como dicionário
02.# {'palavra':..., 'usadas':..., 'tentativas':...}
03.# palavra e usadas são listas de caracteres. tentativas é um inteiro não negativo
04. 
05.# Construtor
06.def cria_estado():
07.    """ Cria estado 'vazio'."""
08.    estado= dict(palavra=[],usadas=[],tentativas=0)
09.    return estado
10.     
11.# Acessores
12.def get_estado(estado):
13.    # Devolve estado
14.    pal = ' '.join(estado['palavra'])
15.    letras = ', '.join(estado['usadas'])
16.    tenta = estado['tentativas']
17.    return (pal, letra,tenta)
18. 
19.def get_pal(estado):
20.    return  ' '.join(estado['palavra'])
21. 
22.def get_letras(estado):
23.    return  ' '.join(estado['usadas'])
24. 
25.def get_tentativas(estado):
26.    return  estado['tentativas']
27. 
28.# Modificadores
29.def set_estado(estado, palavra,letras,tentativas):
30.    # Altera estado
31.    estado['palavra'] = list(palavra)
32.    estado['usadas'] = list(letras)
33.    estado['tentativas'] = tentativas
34.    return estado
35. 
36.def set_pal(estado,pal):
37.    estado['palavra'] = list(pal)
38.    return estado
39. 
40.def set_letras(estado,letras):
41.    estado['usadas'] = list(letras)
42.    return estado
43. 
44.def set_tentativas(estado,tenta):
45.    if tenta >= 0:
46.        estado['tentativas'] = tenta
47.    else:
48.        print 'O valor não pode ser negativo. Foi indicado %d.' % tenta  
49.    return estado
50. 
51.def add_letra_pal(estado, letra,l_pos):
52.    """ Junta a letra em todas as posições não ocupadas indicadas em l_pos."""
53.    for ind in l_pos:
54.        if estado['palavra'][ind] == '_':
55.            estado['palavra'][ind] = letra
56.        else:
57.            print 'Posição %d já ocupada. Estado inalterado!' % ind
58.    return estado
59. 
60.def add_letra_letras(estado, letra):
61.    """ Junta a letra às letras usadas."""
62.    if not letra in estado['usadas']:
63.        estado['usadas'].append(letra)
64.    else:
65.        print '%s já existente. Estado inalterado!' % letra
66.    return estado
67. 
68.def add_tentativas(estado,tenta):
69.    """ Modifica o valor das tentivas em tenta."""
70.    novo_valor = estado['tentativas'] + tenta
71.    if novo_valor >= 0:
72.        estado['tentativas'] = novo_valor
73.    else:
74.        print 'O valor não pode ser negativo. o Resultado foi %d.' % novo_valor   
75.    return estado
76. 
77.# Auxiliares
78.def mostra_estado(estado):
79.    # Mostra palavra
80.    print 'Palavra Actual: ',' '.join(estado['palavra'])
81.    print
82.    # Mostra letras usadas
83.    print 'Letras já usadas: ',', '.join(estado['usadas'])
84.    print
85.    # Mostra tentativas restantes
86.    print 'Ainda tem as tentativas: ', estado['tentativas']
87.    print

Acabamos de mostrar como uma estrutura de dados se define através de grupos de operações. Um (ou mais) construtor, acessores (para o todo ou a parte dos elementos da estrutura), modificadores (do todo ou da parte da estrutura) e, ainda operações auxiliares. Algumas dessas operações são simétricas: uma consulta (get), a outra altera (set ou add). Com base nelas o nosso código para o enforcado pode ser reescrito.

01.def hang89():
02.    # inicialização
03.    # --- palavra secreta
04.    palavras = open('/tempo/data/palavras.txt').read().split()
05.    secreta = list(random.choice(palavras))
06.    dicio = seq_to_dict(secreta)
07.    # --- parâmetros
08.    TAMANHO = len(secreta)
09.    LIMITE = limite(TAMANHO)
10.    estado = cria_estado()
11.    estado = set_estado(estado,'_'* TAMANHO,'',LIMITE)
12.    acertou = False
13.    # Entra no jogo
14.    for tentativa in range(LIMITE):
15.        # estado actual
16.        mostra_estado(estado)
17.        # joga
18.        letra = adivinha(estado['usadas'])
19.        # analisa resposta
20.        if letra in dicio:
21.            # --- Acertou na letra!
22.            indices = dicio[letra]
23.            estado = add_letra_pal(estado,letra,indices)
24.            # --- Acertou na palavra??
25.            if fim(secreta,estado['palavra']):
26.                acertou = True
27.                mensagem_sim(secreta)
28.                break
29.        # actualiza estado
30.        esatdo = add_letra_letras(estado,letra)
31.        estado = add_tentativas(estado, -1)
32.    # mensagem final
33.    mensagem_fim(acertou,secreta)

A pergunta natural que se coloca é a de saber o que ganhámos com estas alterações. A resposta liga-se às duas questões iniciais. Imaginemos que decidimos altera a representação de um dicionário para uma lista. Como proceder? Nesta abordagem basta alterar as operações sobre a estrutura de dados estado. Não precisamos alterar nada no código do programa principal! Programar por camadas de abstracção tem elevados ganhos de produtividade.

Sem comentários:

Enviar um comentário