Introdução à Lógica de Programação

Um curso prático para estudantes das Ciências da Vida


Aula 4. Listas

Instrutor: Pedro C. de Siracusa

Nas aulas anteriores aprendemos como armazenar um valor, de qualquer tipo, em uma variável. Vimos também como isso é útil para organizarmos nosso código, torná-lo mais legível e, principalmente, para aproveitar resultados obtidos em etapas anteriores e reutilizá-los várias vezes durante nosso programa. Mas variáveis só podem ser usadas para referenciar um único valor. E se quiséssemos armazenar um conjunto de valores em uma mesma variável, de forma que pudéssemos acessá-los todos de uma só vez? Para isso recorremos a uma estrutura de dados nativa de Python, chamada lista.

Listas são representadas pelo tipo list, e permitem armazenar um conjunto de valores em uma única estrutura! Cada elemento ocupando uma posição dentro da lista é chamado de item (ou elemento). O número de itens que uma lista armazena é referido como seu comprimento. A princípio não há limites para o comprimento de uma lista, embora listas grandes demais certamente exigem mais recursos computacionais, podendo se tornar intratáveis. Outra característica importante das listas é que elas permitem armazenar itens de tipos diferentes dentro de uma mesma estrutura (embora seja mais usual armazenar itens do mesmo tipo). Podemos por exemplo armazenar dentro de uma única lista strings, booleanos, inteiros, floats, e até outras listas!

Na ilustração acima, temos uma lista de comprimento 5, armazenando itens de tipos diferentes. Cada item da lista ocupa um espaço próprio, representado como um "quadradinho". Os números abaixo de cada quadrado representam os índices de cada item na lista. Os índices nada mais são que endereços numéricos, que nos permitem referenciar elementos em cada posição na lista. Por exemplo, se quisermos nos referir ao número 42, indicaremos o índice (ou endereço) 2.

Obs: Em Python, a numeração dos índices começa em zero, e portanto o primeiro elemento se encontra na posição 0. Da mesma forma, o último item de uma lista de comprimento $n$ se encontra na posição $n-1$.

Objetivos.

Após esta aula você deverá ser capaz de:

  • Reconhecer a utilidade das listas como estruturas de dados;
  • Construir e combinar listas;
  • Recuperar elementos de uma lista.

1. Construindo uma lista

Existem algumas formas diferentes de se construir uma lista em Python. A mais simples consiste em inserir uma sequência de itens separados por vírgulas entre colchetes. Note que a ordem dos itens permanece a mesma em que foram inseridos durante s criação da lista.

In [1]:
# Como exemplo, uma lista contendo um nome, uma idade e uma altura
["Pedro", 29, 1.83]
Out[1]:
['Pedro', 29, 1.83]

Podemos também usar a função list, passando como argumento uma sequência de números inteiros (ou range). Uma range pode ser criada usando a função range.

In [2]:
# Lista de 0 até 10
list(range(11))
Out[2]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [3]:
# Lista de 1 até 10
list(range(1,11))
Out[3]:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [4]:
# Lista de 1 até 10 de 2 em 2
list(range(1,11,2))
Out[4]:
[1, 3, 5, 7, 9]

A linguagem Python fornece outras formas mais eficientes de se criar listas, como o método de compreensão de listas. Mas para manter a simplicidade do material não abordaremos este método por enquanto. Fica apenas a referência para os mais curiosos.

2. Acessando elementos da lista

Agora que temos uma lista, como acessar os elementos dentro dela? Existem basicamente duas formas: a indexação (indexing) e o recorte (slicing). Por fim, podemos usar também algumas funções aplicáveis a listas. Vejamos cada um deles.

Indexing

Usando a notação de indexação [i] podemos recuperar o elemento em determinada posição em uma lista. Para isso basta indicar o índice i do elemento que queremos entre colchetes, ao lado da nossa lista (ou da variável que a armazena). Se tentarmos passar um índice que não exista na lista (por exemplo o índice 9 em uma lista de comprimento 5), o interpretador encerrará a execução do programa e nos mostrará uma mensagem de erro.

In [5]:
l = ["Pedro", 29, 1.83]
In [6]:
l[0]
Out[6]:
'Pedro'
In [7]:
l[1]
Out[7]:
29
In [8]:
l[2]
Out[8]:
1.83
In [9]:
l[3]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-9-bb49eeb9f0db> in <module>()
----> 1 l[3]

IndexError: list index out of range

Podemos usar números negativos para nos referir a elementos na lista seguindo uma lógica de ordem reversa. Por exemplo, podemos usar o índice -1 para nos referir ao último elemento, -2 ao penúltimo, e assim por diante.

In [10]:
l[-1]
Out[10]:
1.83
In [11]:
l[-2]
Out[11]:
29
In [12]:
l[-3]
Out[12]:
'Pedro'

Slicing

Usando a notação de recorte [i:j] podemos recuperar um conjunto de elementos de uma lista. A diferença para a indexação é que o recorte retorna uma sublista (um pedaço, ou slice da lista original), em vez de um único elemento. Para isso indicamos, entre colchetes e separados por dois pontos, dois números: o primeiro (i) é o índice do elemento no começo do recorte; o segundo (j) é o índice do elemento que delimita o fim do pedaço (não-inclusivo).

In [13]:
l = [1,2,3,4,5,6,7,8,9,10]
In [14]:
l[0:3]
Out[14]:
[1, 2, 3]

Podemos omitir o número i ou j se quisermos que o pedaço comece do primeiro elemento da lista ou que termine no último elemento da lista, respectivamente.

In [15]:
l[:3]
Out[15]:
[1, 2, 3]
In [16]:
l[3:]
Out[16]:
[4, 5, 6, 7, 8, 9, 10]

Embora pouco usual, podemos definir o passo do recorte alterando a notação de recorte para [i:j:k]. Neste caso, k é o tamanho do passo, ou seja, o número de elementos que devem ser pulados.

In [17]:
l[2:9:2]
Out[17]:
[3, 5, 7, 9]
In [18]:
# Percorrendo toda a lista de 2 em 2
l[::2]
Out[18]:
[1, 3, 5, 7, 9]
In [19]:
# Percorrendo toda a lista de 3 em 3
l[::3]
Out[19]:
[1, 4, 7, 10]

Funções aplicáveis a listas

E se quisermos obter o elemento de maior ou menor valor dentro da lista? Para isso podemos usar as funções max e min, respectivamente.

In [20]:
l = [34, 5, -1, 8, 43, -7, 0, 1]
In [21]:
max(l)
Out[21]:
43
In [22]:
min(l)
Out[22]:
-7

A função reversed espera receber uma lista e retorna uma nova lista com os elementos em ordem inversa.

In [23]:
l = [1,2,3,4,5,6,7,8,9,10]
In [24]:
list(reversed(l))
Out[24]:
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

Finalmente, podemos verificar se um elemento com determinado valor existe dentro de uma lista. Para isso, construímos uma expressão utilizando o operador in. Caso o elemento exista na lista, a expressão retornará o valor booleano True e, caso contrário, False.

In [25]:
nomes = ["Pedro", "Adriana", "Nicole"]
In [26]:
"Pedro" in nomes
Out[26]:
True
In [27]:
"Adriana" in nomes
Out[27]:
True
In [28]:
"Ana" in nomes
Out[28]:
False

3. Combinando listas

É possível construir listas facilmente a partir de outras. Para isso usamos o operador +, que pode ser usado para concatenar listas. É importante que ambos os operandos sejam do mesmo tipo: lista.

In [29]:
l1 = [1,2,3,4,5]
l2 = ['a','b','c','d','e']

l1 + l2
Out[29]:
[1, 2, 3, 4, 5, 'a', 'b', 'c', 'd', 'e']

Este operador é comumente usado para inserir novos elementos em uma lista. Mas lembre-se que listas só podem ser somadas a listas!

In [30]:
[1,2,3,4,5] + [6]
Out[30]:
[1, 2, 3, 4, 5, 6]
In [31]:
[1,2,3,4,5] + 6 # este erro ocorreu pq tentamos somar um inteiro a uma lista!
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-31-929057959a29> in <module>()
----> 1 [1,2,3,4,5] + 6 # este erro ocorreu pq tentamos somar um inteiro a uma lista!

TypeError: can only concatenate list (not "int") to list

Exercícios.

Ex 1. Você recebeu a seguinte tabela com dados de espécies coletadas pela sua turma na saída de Zoologia dos Vertebrados:

id Localidade Espécie Nome comum Contagem Turma
0 FAL Ameiva ameiva Calango 4 Zoovert 1/2018
1 FAL Phyllomedusa oreades Perereca-arborícola 2 Zoovert 1/2018
2 FAL Artibeus sp. Morcego 4 Zoovert 1/2018
3 FAL Athene cunicularia Coruja-buraqueira 5 Zoovert 1/2018
4 FAL Hypostomus sp. Peixe-cascudo 7 Zoovert 1/2018
5 FAL Cariama cristata Seriema 4 Zoovert 1/2018
6 JBB Ameiva ameiva Calango 1 Zoovert 1/2018
7 JBB Chrysocyon brachyurus Lobo-guará 1 Zoovert 1/2018
8 JBB Cariama cristata Seriema 2 Zoovert 1/2018
9 IBGE Bothrops neuwiedi Jararaca 1 Zoovert 1/2018

(i) Armazene os nomes científicos das espécies coletadas em uma lista, e guarde a referência à lista em uma variável chamada especies. Observe se as listas permitem armazenar mais de um elemento com um mesmo valor.

In [32]:
especies = [ 'Ameiva ameiva','Phyllomedusa oreades', 'Artibeus sp.', 
             'Athene cunicularia', 'Hypostomus sp.', 'Cariama cristata',
             'Ameiva ameiva', 'Chrysocyon brachyurus', 'Cariama cristata',
            'Bothrops neuwiedi' ]
In [33]:
especies
Out[33]:
['Ameiva ameiva',
 'Phyllomedusa oreades',
 'Artibeus sp.',
 'Athene cunicularia',
 'Hypostomus sp.',
 'Cariama cristata',
 'Ameiva ameiva',
 'Chrysocyon brachyurus',
 'Cariama cristata',
 'Bothrops neuwiedi']

(ii) Usando a notação de slicing, obtenha três pedaços da lista especies: o primeiro com as espécies coletadas na FAL, o segundo com as espécies coletadas no JBB e o terceiro com as espécies coletadas no IBGE. Armazene cada uma dessas sublistas em variáveis de nome especies_fal, especies_jbb e especies_ibge, respectivamente.

In [34]:
especies_fal = especies[:6]
especies_fal
Out[34]:
['Ameiva ameiva',
 'Phyllomedusa oreades',
 'Artibeus sp.',
 'Athene cunicularia',
 'Hypostomus sp.',
 'Cariama cristata']
In [35]:
especies_jbb = especies[6:9]
especies_jbb
Out[35]:
['Ameiva ameiva', 'Chrysocyon brachyurus', 'Cariama cristata']
In [36]:
especies_ibge = especies[-1:]
especies_ibge
Out[36]:
['Bothrops neuwiedi']

(iii) Agora vamos guardar cada uma três listas de cada localidade em uma única estrutura! Você deverá construir uma lista armazenando as três listas criadas no item anterior (cada uma por sua vez contendo nomes de espécies). Armazene o resultado em uma variável chamada listas_especies. Em seguida, tente recuperar a lista de espécies para cada uma das localidades usando a variável que você acabou de criar.

In [37]:
listas_especies = [
    especies_fal,
    especies_jbb,
    especies_ibge
]

listas_especies
Out[37]:
[['Ameiva ameiva',
  'Phyllomedusa oreades',
  'Artibeus sp.',
  'Athene cunicularia',
  'Hypostomus sp.',
  'Cariama cristata'],
 ['Ameiva ameiva', 'Chrysocyon brachyurus', 'Cariama cristata'],
 ['Bothrops neuwiedi']]

(iv) Escreva uma função coletadaEm, que recebe como argumentos:

  • o nome científico de alguma espécie;
  • a lista contendo a lista de espécies para cada uma das três localidades (você a criou no item acima).

Sua função deve retornar uma lista com os nomes das localidades em que a espécie passada como argumento ocorre. Em que localidades Cariama cristata ocorre? E Phyllomedusa oreades?

In [38]:
def coletadaEm( especie, listas_especies ):
    res = [] # esta lista começa vazia, e deve ir sendo preenchida ao longo do código
    
    if especie in listas_especies[0]:
        res = res + [ 'fal' ]
      
    if especie in listas_especies[1]:
        res = res + [ 'jbb' ]
    
    if especie in listas_especies[2]:
        res = res + [ 'ibge' ]
    
    return res
In [39]:
# Cariama cristata foi coletada na FAL e no JBB
coletadaEm('Cariama cristata', listas_especies)
Out[39]:
['fal', 'jbb']
In [40]:
# Hypostomus sp. foi coletada apenas na FAL
coletadaEm('Hypostomus sp.', listas_especies)
Out[40]:
['fal']
In [41]:
# Puma concolor não foi coletada em nenhuma das localidades
coletadaEm('Puma concolor', listas_especies)
Out[41]:
[]

Ex 2. Em um levantamento florístico, você definiu um grid espacial de $5 \times 5$ células para registrar a ocorrência de espécies. Para cada espécie, armazenamos os dados em uma matriz da seguinte forma:

  • Cada elemento da matriz representa uma célula do grid;
  • Caso a espécie tenha sido registrada em uma célula do grid, sua presença é registrada como um $1$ no elemento correspondente da matriz;
  • Caso a espécie não tenha sido registrada em uma célula, sua ausência é registrada como um $0$ no elemento correspondente da matriz.

Os dados de ocorrência de Caryocar brasiliense (vulgo Pequi) foram registrados em uma lista de listas! Pare por um instante e tente entender como uma lista de listas pode ser usada para representar uma matriz.

In [42]:
# Apenas execute esta célula
grid_caryocar = [ [1,0,0,1,0],
                  [0,0,0,1,0],
                  [1,0,1,1,1],
                  [0,0,1,0,0],
                  [1,0,1,1,0] ]

Agora, seu desafio é acessar apenas os elementos da matriz acima em que a ocorrência de Caryocar brasiliense foi registrada (ou seja, os $1$s).

In [43]:
grid_caryocar[0][0]
Out[43]:
1
In [44]:
grid_caryocar[0][3]
Out[44]:
1
In [45]:
grid_caryocar[1][3]
Out[45]:
1
In [46]:
grid_caryocar[4][0]
Out[46]:
1