Introdução à programação e aos algoritmos
5.11 Listas de Listas
Tudo o que foi aprendido para vetores e listas vale para ser
usado em matrizes. Por exemplo, podemos definir uma matriz 2 × 3, ou seja, uma matriz com duas linhas e 3 colunas em Python da seguinte forma:
matriz = [[1,2,3],[4,5,6],[7,8,9]]
Essa matriz, pode ser a representação da tabela:
1 2 3
4 5 6
7 8 9
Mas, repare bem, Python cria listas de listas. Não são
verdadeiras matrizes. O que quero dizer com isso? Veja o exemplo: n_matriz = [[1,2,3],[4,5],[7]]
1 2 3
4 5
7
Para ser uma matriz, no sentido matemático do termo, a variável
deveria ter todos as suas linhas completas, indicando, no caso, uma matriz 3 × 3.
É claro, podemos usar essas listas de listas como matrizes,
porém, na maior parte das vezes em que precisarmos fazer operações com matrizes, é mais vantajoso usar um módulo especial chamado numpy, do qual falarei na Seção 5.14.
5.11 LISTAS DE LISTAS
As operações definidas para listas são todas aplicáveis a listas de listas, e mesmo a listas de listas de listas. Você pode ir encaixando listas até o nível em que desejar, porém isso não é uma boa prática de programação. Manter as coisas simples, deve ser sua regra mais importante.
Dica 5.3 – Listas Aninhadas?
Frequentemente você verá o termo “aninhado” com o sentido de “encaixado” em
textos de Computação. Dessa forma, uma matriz, em Python, seria composta de
listas “aninhadas”. Acho este termo uma má tradução do termo original inglês nest.
Sim, nest pode signi car ninho, mas em português o verbo aninhar tem outro
signi cado.
Esta tradução permanece até hoje. Eu mesmo, por vezes, para ser entendido por
gente da área, uso o termo aninhado. Porém, sempre que possível, pre ro o termo
encaixado, que descreve melhor em português o efeito de se colocar uma lista dentro
de outra.
Até mesmo para laços de execução, com for e while, pre ro o termo laços
encaixados . As famosas bonecas russas matrioskas são encaixadas uma dentro da
outra. Ninguém dirá que são “aninhadas”.
Aninhado, com sentido de encaixado, é somente uma má tradução que cou.
Tendo em mente que muitas vezes é melhor utilizar numpy para
operações com matrizes, vamos ver apenas as operações mais corriqueiras em uma lista de listas. Expanda o conceito para níveis de encaixe mais profundos.
Digamos que eu queira criar uma matriz de 3 × 4 elementos
usando um laço. Veja no Programa 5.31.
lin = 3
col = 4
matriz = [None] * lin
for i in range(n):
matriz[i] = [0] * col
print(matriz)
■ Programa 5.31: Cria matriz 3 × 4 com um laço.
O que esse programa faz? Primeiro define os valores de lin e
col . Como o nome sugere, lin representa linhas e col representa colunas. O próximo passo é criar uma lista com lin elementos
None. None é um elemento nulo (Figura 5.10) e quer dizer que não existe nada armazenado nas n posições, apesar de a lista ter sido criada.
Figura 5.10 Cria lista inicial sem elementos válidos.
Depois o laço do for se encarrega de criar mais outras 3 listas,
cada uma com 4 elementos iguais a zero. A Figura 5.11 é uma simplificação. Para ter um modelo mais próximo da realidade, as 3 listas criadas pelo comando for também deveriam ter seus elementos apontando para números, mas isso tornaria a figura muito confusa, com mais um nível de ponteiros.
Figura 5.11 Cria matriz de elementos [0].
Seguindo a lógica já usada para listas unidimensionais, podemos usar a lista por compreensão para criar uma matriz. Relembrando como funciona para listas simples:
lista = []
for item in algum_iterável:
lista.append(Expressão)
é equivalente a:
lista = [Expressão for item in algum_iterável]
Aplicando este esquema, obtemos:
matriz = [[0]*col for i in range(lin)]
Se você quiser realmente explicitar cada laço, você pode escrever algo mais extenso:
matriz = [[0 for j in range(col)] for i in range(lin )]
As duas formas têm resultados equivalentes.
O acesso aos elementos é simples e você pode referenciar uma
linha inteira ou um elemento isolado. Índices negativos também são permitidos.
Dica 5.4 – Cuidado com a referência.
Você pode ter reparado que um trecho de programa:
[0 for j in range(col)]
foi substituído por algo bem prático e compacto:
[0]*col
Diante disso, você poderia pensar: por que não usar o mesmo princípio para o
segundo laço, obtendo uma expressão ainda mais compacta? Assim:
matriz = [[0]*col]*lin #Errado!!!
Parece bem promissor, e se você imprimir a matriz, obterá um resultado
aparentemente correto:
matriz : [[0, 0, 0, 0], [0, 0, 0, 0],
[0, 0, 0, 0]]
O problema aparece quando você modi ca algum elemento de matriz, como
matriz[0][1] = 1
O resultado não é bem o que você esperava:
matriz : [[0, 1, 0, 0], [0, 1, 0, 0],
[0, 1, 0, 0]]
Você consegue imaginar o que aconteceu? A Figura 5.12 pode lançar uma luz
sobre esta dúvida.