MAC2166 - Introdução à Computação

22/06/2017 - Aula 23 - Dicionários

Em aulas anteriores e nos EPs, vimos tipos de dados para lidar com coleções sequenciais, nas quais os elementos na coleção estão ordenados da esquerda para a direita e os seus valores são acessados por meio de índices que são números inteiros. As listas (list), as strings (str) e as tuplas (tuple) são coleções sequenciais.

Agora veremos um outro tipo de dados do python - o dict (de dictionary, ou dicionário em português) - que também é usado para representar coleções. Entretanto, em um dict a organização dos elementos não é sequencial.

Um dicionário é um tipo abstrato de dados que associa uma chave a um valor. A chave precisa ser única, ou seja, não pode haver chaves repetidas dentro do dicionário. A chave também deve ser imutável, ou seja, uma vez criada, ela permanece a mesma. Portanto, pode-se usar como chave strings, números e tuplas, mas não listas. Já o valor pode ser de qualquer tipo do python.

Para criar um dicionário vazio chamado dic, você pode fazer:

In [1]:
dic = {}

Observe que os elementos de um dicionário são delimitados por chaves ({}), enquanto que os itens de uma lista são delimitados por colchetes ([]).

Para criar um dicionário já com elementos dentros, basta colocar os elementos entre as chaves separados por vírgulas, e usando : (dois pontos) para separar a chave do valor, como mostrado no exemplo abaixo:

In [3]:
disciplinas = {'MAC2166':'Introdução à Computação', 'MAT2453':'Cálculo Diferencial e Integral I'}
print("O conteúdo do meu dicionário de disciplinas é:\n", disciplinas)
O conteúdo do meu dicionário de disciplinas é:
 {'MAC2166': 'Introdução à Computação', 'MAT2453': 'Cálculo Diferencial e Integral I'}

Para ler, criar ou modificar um elemento do dicionário, temos que usar a chave do elemento entre colchetes, como nos exemplos abaixo:

In [4]:
estoque = {'banana':50, 'maçã':4, 'abacaxi':10}    # cria um dicionário com 3 elementos
print("Dicionário inicial:", estoque)
estoque['melancia'] = 7    # adiciona no dicionário um novo elemento: o valor 7 associado à chave 'melancia'  
print("Dicionário depois da adição de novo elemento:", dic)
estoque['banana'] += 15    # modifica o valor assocido à chave 'banana'
print("Dicionário depois da modificação do valor de um elemento:", estoque)
print("Valor associado à chave 'abacaxi' no dicionário:", estoque['abacaxi'])   # lê o valor associado à 'abacaxi'
Dicionário inicial: {'banana': 50, 'maçã': 4, 'abacaxi': 10}
Dicionário depois da adição de novo elemento: {'banana': 50, 'maçã': 4, 'abacaxi': 10}
Dicionário depois da modificação do valor de um elemento: {'banana': 65, 'maçã': 4, 'abacaxi': 10, 'melancia': 7}
Valor associado à chave 'abacaxi' no dicionário: 10

Quando tentamos obter o valor associado à uma chave que não existe no dicionário, uma exceção do tipo KeyError é gerada (veja o exemplo abaixo):

In [5]:
estoque = {'banana':50, 'maçã':4, 'abacaxi':10} 
print(estoque['pera'])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-5-6af3ff42d90d> in <module>()
      1 estoque = {'banana':50, 'maçã':4, 'abacaxi':10}
----> 2 print(estoque['pera'])

KeyError: 'pera'

Podemos verificar se uma dada chave existe em um dicionário usando o operador in, ou ainda verificar se uma chave não existe no dicionário usando not in. Veja o exemplo abaixo:

In [7]:
estoque = {'banana':50, 'maçã':4, 'abacaxi':10} 
if 'pera' in estoque:   # verifica se a chave 'pera' existe no dicionário estoque
    print("A quantidade de peras no estoque é:", estoque['pera'])
else:
    print("Não vendemos peras!")
    if 'abacate' not in estoque:
        print("E também não vendemos abacates.")
Não vendemos peras!
E também não vendemos abacates.

Percorrendo os elementos de um dicionário

Há diferentes formas de se percorrer todos os elementos de um dicionário usando um laço for. Podemos usar o for para iterar sobre as chaves contidas no dicionário e, a partir de cada chave, acessar o valor associado a ela no dicionário. Veja abaixo um exemplo disso:

In [18]:
estoque = {'banana': 50, 'laranja': 12, 'melancia': 7, 'kiwi': 23}

for chave in estoque:
    valor = estoque[chave]
    print("Temos %d unidades de %s no estoque."%(valor, chave))
Temos 50 unidades de banana no estoque.
Temos 12 unidades de laranja no estoque.
Temos 7 unidades de melancia no estoque.
Temos 23 unidades de kiwi no estoque.

Uma forma menos compacta para se iterar sobre as chaves de um dicionário (mas talvez mais legível para alguns) é usando o método keys() de dict, como mostrado abaixo:

In [16]:
for chave in estoque.keys():
    valor = estoque[chave]
    print("Temos %d unidades de %s no estoque."%(valor, chave))
Temos 50 unidades de banana no estoque.
Temos 12 unidades de laranja no estoque.
Temos 7 unidades de melancia no estoque.
Temos 23 unidades de kiwi no estoque.

É possível também iterar sobre os valores de um dicionário utilizando o método values() de dict. Assim, para saber quantas frutas há no estoque podemos escrever:

In [17]:
total = 0
for valor in estoque.values():
    total += valor

print("Temos %d frutas no estoque."%(total))
Temos 92 frutas no estoque.

Por fim, é possível iterar sobre os pares (chave,valor) de um dicionário usando o método items() de dict. Veja o exemplo abaixo:

In [19]:
for (fruta,quantidade) in estoque.items():
    print("Temos %d unidades de %s no estoque."%(quantidade, fruta))
Temos 50 unidades de banana no estoque.
Temos 12 unidades de laranja no estoque.
Temos 7 unidades de melancia no estoque.
Temos 23 unidades de kiwi no estoque.

O conteúdo apresentado nesta aula é baseado no material disponível aqui e também aqui.

Problema 23.1

Escreva um programa que leia uma frase digitada pelo usuário e calcule a frequência relativa de cada letra na frase, usando dicionários. Ignore caracteres brancos e pontuação.

Solução

In [5]:
def main():
    frase = input("Digite uma frase:\n")
    
    freq_letras = {}   # cria um dicionário vazio

    for caractere in frase:   # para cada caractere na frase, faça

        if caractere not in ' .,;:?!\t\n':    # se o caractere atual não é um caractere que deve ser ignorado
            if caractere in freq_letras:      # verifica se o caractere já está no dicionário
                freq_letras[caractere] += 1   # Sim, então incrementa a sua frequência
            else:
                freq_letras[caractere] = 1    # Não, então inclui o caractere no dicionário, 
                                              # indicando 1 como frequência

    for (letra,freq) in freq_letras.items():
        print("A frequência da letra '%c' na frase é %d" %(letra, freq))
        

main()
Digite uma frase:
Batatinha quando nasce, espalha a rama pelo chão!
A frequência da letra 'l' na frase é 2
A frequência da letra 'a' na frase é 10
A frequência da letra 'r' na frase é 1
A frequência da letra 's' na frase é 2
A frequência da letra 'q' na frase é 1
A frequência da letra 'i' na frase é 1
A frequência da letra 'p' na frase é 2
A frequência da letra 'h' na frase é 3
A frequência da letra 'e' na frase é 3
A frequência da letra 'd' na frase é 1
A frequência da letra 'm' na frase é 1
A frequência da letra 'B' na frase é 1
A frequência da letra 'u' na frase é 1
A frequência da letra 'n' na frase é 3
A frequência da letra 't' na frase é 2
A frequência da letra 'c' na frase é 2
A frequência da letra 'o' na frase é 3
A frequência da letra 'ã' na frase é 1

Matrizes Esparsas como Dicionários

Matrizes esparsas tipicamente possuem zero em quase todas as posições, e valores diferentes de zero em apenas algumas posições.

Uma matriz esparsa pode ser representada como um dicionário, utilizando as coordenadas como chave.

Por exemplo, a matriz esparsa abaixo

00005
0130-10
00000
004200

poderia ser representada pelo seguinte dicionário em python:

In [17]:
mat_esparsa = {(0,4):5, (1,1):13, (1,3):-1, (3,3):42}

for (coordenada,valor) in mat_esparsa.items():
    print("A posição", coordenada, "da matriz esparsa contém o valor ", valor)
A posição (3, 3) da matriz esparsa contém o valor  42
A posição (1, 3) da matriz esparsa contém o valor  -1
A posição (1, 1) da matriz esparsa contém o valor  13
A posição (0, 4) da matriz esparsa contém o valor  5

Problema 22.2

Como exemplo, vamos implementar um jogo de batalha naval onde o mapa será representado como uma matriz esparsa mantida em um dicionário, com:

  • um submarino valendo 100 pontos,
  • um cruzador valendo 30 pontos (cada pedaço),
  • um porta-aviões valendo 20 pontos (cada pedaço).

O “mar” onde estão as embarcações tem coordenadas de 0 a 99, tanto no eixo x quanto no eixo y.

A partir do esqueleto abaixo, escreva um programa que carrega um mapa em um dicionário usando a função dada carrega_mapa e leia coordenadas do usuário até que ele desista ou até que não reste mais pontos para serem ganhos (ou seja, até que ele afunde todas as embarcações).

Para facilitar a detecção do fim do jogo, implemente uma função total_pontos que retorna o total de pontos ainda disponível no dicionário.

Antes de terminar o programa, imprima quantos pontos o usuário ganhou.

In [13]:
def main():
    # edite o código abaixo para resolver este exercício
    mapa = carrega_mapa()
    print(mapa)

    
def total_pontos( dic ):
    ''' (dict) -> int '''
    # escreva a função
    

def carrega_mapa():
    ''' Retorna um dicionário contendo as coordenadas das
    embarcações. O mapa poderia ser lido, por exemplo, de um
    arquivo, ou as posições das embarcações poderiam ser
    sorteadas.
        Na implementação atual, o mapa está fixo.
    '''
    return {(2,5):100, # submarino
            (20,60):30, (21,60):30, # cruzador
            (42,71):20, (43,71):20, (44,71):20, (45,71):20} # porta-aviões
            

main()
{(43, 71): 20, (20, 60): 30, (45, 71): 20, (44, 71): 20, (42, 71): 20, (2, 5): 100, (21, 60): 30}

Solução

Na estratégia de implementação mostrada abaixo, modifica-se o valor associado a uma posição definida no dicionário para zero toda vez que o usuário acertar uma embarcação.

In [16]:
def main():

    mapa = carrega_mapa()
    fim = False
    pontos = 0
    total = total_pontos(mapa)
    print("Batalha Naval")
    print("Você pode fazer até %d pontos\n"%total)

    print("Digite as coordenadas separadas por vírgula")
    while not fim:
        x, y = input("Digite x, y (ou -1,-1 para terminar): ").split(',')
        x, y = int(x), int(y)
        print("Você jogou em: %d,%d" %(x,y))
        
        if (x,y) == (-1,-1):
            # Usuário pediu para encerrar o jogo
            fim = True
        elif (x,y) in mapa and mapa[x,y] > 0:   
            # (x,y) é a coordenada de um pedaço de embarcação ainda não atingido
            valor = mapa[x,y]
            print("Voce acertou uma embarcação e fez %d pontos" %valor)
            pontos += valor
            mapa[(x,y)] = 0    # troca o valor da posição para zero para indicar que foi atingida
            
            if total_pontos(mapa) == 0:
                # Usuário já ganhou todos os pontos possíveis
                fim = True
        else:
            print("Água!!")

    print("Você fez %d pontos" %pontos)

#----------------------------------------------------
def total_pontos(dic):
    ''' (dict) -> int '''
    soma = 0
    for coordenada in dic:
        soma += dic[coordenada]
    return soma

#----------------------------------------------------
def carrega_mapa():
    ''' Retorna um dicionário contendo as coordenadas das
    embarcações. O mapa poderia ser lido, por exemplo, de um
    arquivo, ou as posições das embarcações poderiam ser
    sorteadas.
        Na implementação atual, o mapa está fixo.
    '''
    return {(2,5):100, # submarino
            (20,60):30, (21,60):30, # cruzador
            (42,71):20, (43,71):20, (44,71):20, (45,71):20} # porta-aviões
            
#----------------------------------------------------
main()
Batalha Naval
Você pode fazer até 240 pontos

Digite as coordenadas separadas por vírgula
Digite x, y (ou -1,-1 para terminar): 89,34
Você jogou em: 89,34
Água!!
Digite x, y (ou -1,-1 para terminar): 44,71
Você jogou em: 44,71
Voce acertou uma embarcação e fez 20 pontos
Digite x, y (ou -1,-1 para terminar): 42,71
Você jogou em: 42,71
Voce acertou uma embarcação e fez 20 pontos
Digite x, y (ou -1,-1 para terminar): 40,71
Você jogou em: 40,71
Água!!
Digite x, y (ou -1,-1 para terminar): 21,60
Você jogou em: 21,60
Voce acertou uma embarcação e fez 30 pontos
Digite x, y (ou -1,-1 para terminar): -1,-1
Você jogou em: -1,-1
Você fez 70 pontos