MAC2166 - Introdução à Computação

27/04/2017 - Aula 10

Copiando listas

Quando fazemos uma de atribuição de uma variável do tipo list para uma outra variável, precisamos ter cuidado. A operação de atribuição nesse caso não faz uma cópia da lista na nova variável, ela apenas cria uma outra referência para a mesma lista. Veja o exemplo a seguir:

In [3]:
lista1 = [20, 40, 30, 10]
print("Lista 1:", lista1)
lista2 = lista1
print("Lista 2:", lista2)
print("Atribuindo um novo valor à primeira posição da Lista 1...")
lista1[0] = 50       # modifica primeira posição de lista1
print("Lista 1:", lista1)
print("Lista 2:", lista2)
print("Inclindo um novo elemento na Lista 2...")
lista2.append(60)
print("Lista 1:", lista1)
print("Lista 2:", lista2)
Lista 1: [20, 40, 30, 10]
Lista 2: [20, 40, 30, 10]
Atribuindo um novo valor à primeira posição da Lista 1...
Lista 1: [50, 40, 30, 10]
Lista 2: [50, 40, 30, 10]
Inclindo um novo elemento na Lista 2...
Lista 1: [50, 40, 30, 10, 60]
Lista 2: [50, 40, 30, 10, 60]

Pelo exemplo acima, é possível ver que uma modificação em lista1 é refletida também em lista2 e vice-versa. Na verdade, as variáveis lista1 e lista2 são apenas dois nomes diferentes para uma mesma lista.

Esse tipo de comportamento não acontece com variáveis dos tipos int e float. Veja o exemplo a seguir:

In [4]:
num1 = 20
num2 = num1
print(num1, num2)
num1 = 30
print(num1, num2)
num2 = 50
print(num1, num2)
20 20
30 20
30 50

Diante do exposto acima, qual é a forma correta de se copiar uma lista (ou seja, criar uma nova lista com os mesmos elementos de uma lista já existente)? A função clone() mostrada abaixo exemplifica uma forma de se fazer isso.

In [2]:
def clone(lista):
    """ (list) --> list
    
    Cria e retorna um clone da lista recebida como parâmetro.
    
    """
    clone = []
    for i in range(len(lista)):
        clone.append(lista[i])
        
    return clone


# Testa a função clone
lista3 = [0,1,2,3,4,5,6]
copia_lista3 = clone(lista3)   # cria uma cópia de lista3 na variável copia_lista3
print("Lista:", lista3)
print("Cópia da lista:", copia_lista3)
lista3[0] = 100
print("Lista modificada:", lista3)
print("Mas a cópia da lista não foi afetada:", copia_lista3)
Lista: [0, 1, 2, 3, 4, 5, 6]
Cópia da lista: [0, 1, 2, 3, 4, 5, 6]
Lista modificada: [100, 1, 2, 3, 4, 5, 6]
Mas a cópia da lista não foi afetada: [0, 1, 2, 3, 4, 5, 6]

Uma outra forma mais simples de criar uma cópia de uma lista é usando o operador o operador de "fatiar" listas :. Veja o exemplo a seguir:

In [2]:
lista3 = [0,1,2,3,4,5,6]
copia_lista3 = lista3[:]   # cria uma cópia de lista3 na variável copia_lista3
print("Lista:", lista3)
print("Cópia da lista:", copia_lista3)
lista3[0] = 100
print("Lista modificada:", lista3)
print("Mas a cópia da lista não foi afetada:", copia_lista3)
Lista: [0, 1, 2, 3, 4, 5, 6]
Cópia da lista: [0, 1, 2, 3, 4, 5, 6]
Lista modificada: [100, 1, 2, 3, 4, 5, 6]
Mas a cópia da lista não foi afetada: [0, 1, 2, 3, 4, 5, 6]

O exemplo acima mostra o operador : sendo usado em um caso particular, para criar uma cópia de uma lista. Mas, ele pode ser usado para criar uma nova lista a partir de qualquer "fatia" de elementos de uma outra lista.Veja alguns exemplos:

In [9]:
lista4 = [0,1,2,3,4,5,6]
lista5 = lista4[2:5]   # Cria uma nova lista contendo os elementos das pos. de 2 até a 4 (inclusive) da lista4  
lista6 = lista4[1:4]   # Cria uma nova lista contendo os elementos das pos. de 1 até a 2 (inclusive) da lista4  
print("L4:", lista4)
print("L5:", lista5)
print("L6:", lista6)
lista4[3] = 100
lista5[1] = 200
lista6[2] = 300
# Mostra que as modificações feitas em uma das listas não afetam as demais
print("L4:", lista4)
print("L5:", lista5)
print("L6:", lista6)
L4: [0, 1, 2, 3, 4, 5, 6]
L5: [2, 3, 4]
L6: [1, 2, 3]
L4: [0, 1, 2, 100, 4, 5, 6]
L5: [2, 200, 4]
L6: [1, 2, 300]

Quando fazemos lista[:], estamos usando o operador : para criar uma nova lista contendo a "fatia completa" de uma outra lista. Mas também podemos usar esse operador sem restringir apenas um dos limites, como nos exemplos abaixo:

In [13]:
lista7 = [0,1,2,3,4,5,6]
print(lista7[3:])   # imprime os elementos da lista a partir da posição 3
print(lista7[:5])   # imprime os elementos da lista até a posição 4
[3, 4, 5, 6]
[0, 1, 2, 3, 4]

Problema 10.1

Dados dois números naturais m e n e duas sequências ordenadas com m > 0 e n > 0 números inteiros, criar uma nova lista contendo a sequência ordenada com todos os elementos das sequências originais sem repetição.

Obs.: Este problema corresponde ao exercício 8 da lista de exercícios sobre vetores.

Solução 1

In [4]:
def main():
    # Lê os elementos da primeira sequência 
    m = int(input("Digite m: "))
    cont1 = 0
    seq1 = []
    while cont1 < m:
        cont1 += 1
        num = int(input("Digite o número %d da sequencia 1: "%(cont1)))
        seq1.append(num)

    print()

    # Lê os elementos da segunda sequencia 
    n = int(input("Digite n: "))
    cont2 = 0
    seq2 = []
    while cont2 < n:
        cont2 += 1
        num = int(input("Digite o número %d da sequencia 2: "%(cont2)))
        seq2.append(num)

    # Cria a sequencia ordenada a partir das duas outras
    seq_ordenada = []    

    cont1 = 0
    cont2 = 0

    # Intercala os elementos de forma ordenada até que uma das listas chegue ao fim
    while cont1 < m and cont2 < n:
        if seq1[cont1] <= seq2[cont2]:
            # Só insere se não for repetido
            if len(seq_ordenada) == 0 or seq1[cont1] != seq_ordenada[-1]:
                seq_ordenada.append(seq1[cont1])
            cont1 += 1
        else:
            if len(seq_ordenada) == 0 or seq2[cont2] != seq_ordenada[-1]:
                seq_ordenada.append(seq2[cont2])
            cont2 += 1

    # Inclui na lista ordenada os elementos restantes da lista que não foi 
    # percorrida até o fim no laço anterior
    while cont1 < m:
        if len(seq_ordenada) == 0 or seq1[cont1] != seq_ordenada[-1]:
            seq_ordenada.append(seq1[cont1])
        cont1 += 1

    while cont2 < n:
        if len(seq_ordenada) == 0 or seq2[cont2] != seq_ordenada[-1]:
            seq_ordenada.append(seq2[cont2])
        cont2 += 1

    print(seq_ordenada)

#---------------------------------------
main()
Digite m: 5
Digite o número 1 da sequencia 1: 1
Digite o número 2 da sequencia 1: 3
Digite o número 3 da sequencia 1: 3
Digite o número 4 da sequencia 1: 4
Digite o número 5 da sequencia 1: 4

Digite n: 4
Digite o número 1 da sequencia 2: 2
Digite o número 2 da sequencia 2: 3
Digite o número 3 da sequencia 2: 3
Digite o número 4 da sequencia 2: 5
[1, 2, 3, 4, 5]

Solução 2 (usando funções)

In [1]:
def main():
    '''
    Dados m e n e duas sequencias ordenadas com m e n numeros inteiros
    criar uma nova lista ordenada com todos os elementos das sequencias originais
    sem repeticoes
    '''

    # le os elementos da primeira sequencia
    print("Iniciando leitura da primeira sequencia: ")
    seq_1 = le_sequencia()
    print("Sequencia 1:", seq_1)

    # le os elementos da segunda sequencia
    print("Iniciando leitura da segunda sequencia ordenada: ")
    seq_2 = le_sequencia()
    print("Sequencia 2:", seq_2)

    # intercale as duas sequencia eleminando as possiveis repeticoes
    # neste cada seq_i eh formada por elementos distintos.
    seq_final = intercala(seq_1,seq_2)

    print("Sequencia ordenada e sem repeticoes:", seq_final)

#------------------------------------------------------------
def le_sequencia():
    '''(None) -> list

    Le do teclado uma sequencia de numeros inteiros e retorna
    uma lista com os elementos lidos.

    Observacoes: a lista retornada nao tem elementos repetidos.
    '''
    seq = []
    n = int(input("Digite o tamanho da sequencia:"))
    for i in range(n):
        # le o proximo numero da sequencia
        num = int(input("Digite o %do. numero da sequencia: " %(i+1)))

        # procura o numero lido na lista
        achou = pertence(num,seq) # equivalente a: achou = num in seq

        # verifica se o numero lido deve ser inserido na lista
        if not achou:
            seq.append(num)

    return seq

#-------------------------------------------------
def intercala(lista_1, lista_2):
    '''(list, list) -> list

    Recebe lista_1 e lista_2 que sao listas ordenadas de numeros.
    Retorna uma lista ordenada dos numeros contido nas listas
    dadas, sem repeticoes.
    
    '''
    lista = []
    i = 0 # percorre lista 1
    len_1 = len(lista_1)
    j = 0 # percorre lista 2
    len_2 = len(lista_2)
    while i < len_1 or j < len_2:
        if i == len_1:   # ja incluiu na nova lista todos os itens da lista 1
            lista.append(lista_2[j])
            j += 1
        elif j == len_2:   # ja incluiu na nova lista todos os itens da lista 2
            lista.append(lista_1[i])
            i += 1
        elif lista_1[i] == lista_2[j]:  
            lista.append(lista_1[i])
            i += 1
            j += 1
        elif lista_1[i] < lista_2[j]:
            lista.append(lista_1[i])
            i += 1
        else:
            lista.append(lista_2[j])
            j += 1

    return lista

#-------------------------------------------------
def pertence(item,lista):
    '''(objeto, list) -> bool

    Recebe uma lista de itens e um item e
    retorna True se o item eh um elemento da lista e
    False em caso contrario.
    '''
    achou = False
    j = 0
    while j < len(lista) and not achou:
        if item == lista[j]:
            achou = True
        j = j + 1

    return achou

#---------------------------------------
main()
Iniciando leitura da primeira sequencia: 
Digite o tamanho da sequencia:5
Digite o 1o. numero da sequencia: 1
Digite o 2o. numero da sequencia: 3
Digite o 3o. numero da sequencia: 3
Digite o 4o. numero da sequencia: 4
Digite o 5o. numero da sequencia: 4
Sequencia 1: [1, 3, 4]
Iniciando leitura da segunda sequencia ordenada: 
Digite o tamanho da sequencia:4
Digite o 1o. numero da sequencia: 2
Digite o 2o. numero da sequencia: 3
Digite o 3o. numero da sequencia: 3
Digite o 4o. numero da sequencia: 5
Sequencia 2: [2, 3, 5]
Sequencia ordenada e sem repeticoes: [1, 2, 3, 4, 5]

Problema 10.2

Dada uma sequência de n números reais, escreva um programa que conte quantas vezes cada número ocorre na sequência.

Solução 1 (usando a função indice())

In [5]:
def main():
    '''
    Dados n e uma sequencia com n numeros reais,
    conta e imprime o numero de vezes que cada
    numero ocorre na sequencia.
    '''
    n = int(input("Digite o tamanho da sequencia: "))

    lista_numeros = []
    cont_ocorrencias = []

    for i in range(n):
        numero = float(input("Digite um numero: "))
        j = indice(numero,lista_numeros)
        
        if j == None:
            lista_numeros.append(numero)
            cont_ocorrencias.append(1)
        else:
            cont_ocorrencias[j] += 1

    print("    Numero  no. ocorrencias")
    for i in range(len(lista_numeros)):
        print("%10.2f      %6d" %(lista_numeros[i],cont_ocorrencias[i]))

#---------------------------------------------------
def indice(item, lista):
    '''(objeto,list) -> int ou None

    Recebe um objeto 'item' e uma lista 'lista' e retorna o
    indice da posicao em que item ocorre na lista.
    Caso item nao ocorra na lista a funcao retorna None
    '''
    # procure item na lista
    n = len(lista)
    for i in range(n):
        if item == lista[i]:
            return i

    # nao encontrou item
    return None

#---------------------------------------------------
main()
Digite o tamanho da sequencia: 5
Digite um numero: 1
Digite um numero: 2
Digite um numero: 2
Digite um numero: 1
Digite um numero: 2
    Numero  no. ocorrencias
      1.00           2
      2.00           3

Solução 2 (usando a função insere_novo() )

In [13]:
def main():
    '''
    Dados n e uma sequencia com n numeros reais,
    conta e imprime o numero de vezes que cada
    numero ocorre na sequencia.
    '''
    n = int(input("Digite o tamanho da sequencia: "))

    lista_numeros = []
    cont_ocorrencias = []

    for i in range(n):
        x = float(input("Digite um numero: "))
        j = insere_novo(x,lista_numeros)
        if j > len(cont_ocorrencias)-1:
            cont_ocorrencias.append(1)
        else:
            cont_ocorrencias[j] += 1

    print("    Numero  no. ocorrencias")
    for i in range(len(lista_numeros)):
        print("%10.2f      %6d" %(lista_numeros[i],cont_ocorrencias[i]))

#---------------------------------------------------
def insere_novo(item, lista):
    '''(objeto,list) -> int

    Recebe um objeto item e uma lista `lista` e retorna o
    indice da posicao em que item ocorre na lista.
    Caso item nao ocorra na lista, ele eh inserido no final e
    o indice dessa posicao eh retornado.
    '''
    # procura item na lista
    n = len(lista)
    for i in range(n):
        if item == lista[i]:
            return i

    # o item nao foi encontrado, entao ele sera inserido no final da lista
    lista.append(item)

    return n

#---------------------------------------------------
main()
Digite o tamanho da sequencia: 5
Digite um numero: 1
Digite um numero: 2
Digite um numero: 2
Digite um numero: 1
Digite um numero: 2
    Numero  no. ocorrencias
      1.00           2
      2.00           3

Observação: no Python, é possível usar a função index() de um objeto do tipo List para obter o índice de um dado elemento dentro de uma lista (ou seja, index() funciona de modo semelhante à função indice() implementada acima). Também há o operador in, que verifica se um dado elemento está em uma lista (portanto, corresponde à função pertence(), implementada em um exercício da aula 9). Mas, como já dito na aula anterior, é muito importante saber percorrer uma lista da forma tradicional, usando um laço while ou for, já que muitas linguagens não possuem uma função equivalente à index() ou ao operador in. Alguns exemplos de uso do operador in e da função index() são mostrados abaixo:

In [10]:
lista = ["oi", 1, 3.14, "Maçã", 7, False]
print("'oi' in lista", "oi" in lista)
print("'oi' not in lista", "oi" not in lista)
print("not 'oi' in lista", not "oi" in lista)

if 5 in lista:
    print("5 está na lista")
else:
    print("5 não está na lista")

print("'Maçã' está na posicao %d da lista" %(lista.index("Maçã")))

if lista.index("oi") == 0:
    print("'oi' é o primeiro elemento da lista")
'oi' in lista True
'oi' not in lista False
not 'oi' in lista False
5 não está na lista
'Maçã' está na posicao 3 da lista
'oi' é o primeiro elemento da lista

Sobre o valor especial None

Python possui um valor especial, o None, que pode ser usado para representar a ausência de valor.

O None é útil, por exemplo, quando queremos indicar que uma dada posição de uma lista está vazia (ou seja, não possui um valor associada a ela). O tipo do valor None é NoneType; aliás, ele é o único valor existente desse tipo.

Veja os exemplos a seguir:

In [5]:
valor = None
print(valor, "-->", type(valor))
lista = [1,2,3,None,5,None]
print(lista," --> tamanho da lista:",len(lista))
None --> <class 'NoneType'>
[1, 2, 3, None, 5, None]  --> tamanho da lista: 6

Importante: Não confundir o valor especial None com as strings "None" ou "none". O valor especial é escrito sem aspas e com a primeira letra em maiúsculo.

In [6]:
valor1 = "None"   # Essa é uma string, não é o valor especial None
print("Tipo do valor 1:", type(valor1))
valor2 = None     # Esse é o valor especial None
print("Tipo do valor 2:", type(valor2))
valor1 == valor
Tipo do valor 1: <class 'str'>
Tipo do valor 2: <class 'NoneType'>
Out[6]:
False

Não podemos fazer operações matemáticas envolvendo o None como operador (ele não é equivalente ao valor zero!). Veja a seguir um exemplo de erro gerado quando tentamos fazer operações matemáticas com o None:

In [7]:
valor1 = 12
valor2 = None
valor1 + valor2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-8252db275da0> in <module>()
      1 valor1 = 12
      2 valor2 = None
----> 3 valor1 + valor2

TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

Quando precisamos fazer operações matemáticas envolvendo os elementos de uma lista que pode conter posições com o valor None, então é sempre preciso verificar o valor antes de envolvê-lo em algum cálculo, como mostrado no exemplo a seguir:

In [8]:
lista = [1,2,3,None,5,None]
# vamos fazer um laço para somar os elementos da lista
soma = 0
for elemento in lista:
    if elemento != None:  # só soma o elemento se ele não for None
        soma += elemento
print(soma)
11

Tópicos vistos na Aula 10

  • Diferença entre criar uma cópia de uma lista e criar uma nova referência para uma lista
  • O operador para fatiar listas :
  • Funções que recebem listas como parâmetros ou que devolvem listas