Aula 11: Funções com listas e fatiamento de listas

Tópicos

Aulas a distância - Google Meet:

Para verem os vídeos, vocês devem estar logados no e-mail da usp.

Veja o vídeo abaixo sobre manipulação de listas em Python:

Fatias de listas

Para uma dada lista, podemos criar uma sublista formada por uma cópia de um trecho de elementos consecutivos da primeira através do recurso de fatiamento de listas. Para isso, usamos a seguinte sintaxe: lista[início:fim]

Caso omitido, o parâmetro início é assumido como sendo 0 (zero) e o fim, quando omitido, é assumido como sendo len(lista).

Veja os exemplos abaixo digitados no Python Shell:

>>> cidades = ['Bento Gonçalves', 'Campos do Jordão', 'Gramado', 'Ouro Preto', 'Fortaleza', 'Maceió', 'Rio de Janeiro']
>>> cidades[2:6]
['Gramado', 'Ouro Preto', 'Fortaleza', 'Maceió']
>>> cidades[1:3]
['Campos do Jordão', 'Gramado']
>>> cidades[:4]
['Bento Gonçalves', 'Campos do Jordão', 'Gramado', 'Ouro Preto']
>>> cidades[3:]
['Ouro Preto', 'Fortaleza', 'Maceió', 'Rio de Janeiro']

Por convenção do Python, o início indica o índice do primeiro elemento que será incluído no trecho copiado, mas o último elemento incluído no trecho é dado por fim-1. Ou seja, o elemento cujo índice é dado por fim nunca é incluído no trecho copiado.

Em especial, o comando lista[:] (ou lista[0:len(lista)]) gera uma cópia da lista toda, podendo ser usado em operações de clonagem de uma lista, no lugar do comando list(lista) visto anteriormente:

>>> cidades = ["Bento Gonçalves", "Campos do Jordão", "Gramado"]
>>> B = cidades[:]
>>> id(cidades) == id(B)
False
>>> B.append("Ouro Preto")
>>> print(B)
['Bento Gonçalves', 'Campos do Jordão', 'Gramado', 'Ouro Preto']
>>> print(cidades)
['Bento Gonçalves', 'Campos do Jordão', 'Gramado']

Remoção em listas

Remoções em listas podem ser feitas usando o comando del ou o método pop.
A sintaxe do del é del lista[índice].

>>> cidades = ["Bento Gonçalves", "Campos do Jordão", "Gramado", "Ouro Preto"]
>>> i = 1 #índice a ser removido
>>> del cidades[i]
>>> print(cidades)
['Bento Gonçalves', 'Gramado', 'Ouro Preto']

A sintaxe do pop é lista.pop(índice).
A diferença é que o pop devolve o valor removido da lista, que pode, por exemplo, ser atribuído a uma variável.
Caso omitido o índice, o último elemento é removido.

>>> cidades = ["Bento Gonçalves", "Campos do Jordão", "Gramado", "Ouro Preto"]
>>> i = 1 #índice a ser removido
>>> c = cidades.pop(i)
>>> print(cidades)
['Bento Gonçalves', 'Gramado', 'Ouro Preto']
>>> print(c)
Campos do Jordão
>>> c = cidades.pop()
>>> print(cidades)
['Bento Gonçalves', 'Gramado']
>>> print(c)
Ouro Preto

O efeito da remoção pode também ser obtido construindo uma nova lista sem o elemento indesejado, usando fatiamento mais concatenação.

>>> cidades = ["Bento Gonçalves", "Campos do Jordão", "Gramado", "Ouro Preto"]
>>> i = 1 #índice a ser removido
>>> cidades = cidades[:i] + cidades[i+1:]
>>> print(cidades)
['Bento Gonçalves', 'Gramado', 'Ouro Preto']

Note, porém, que essa última solução é mais custosa, por envolver a criação de uma cópia da lista toda, sendo que a variável cidades passará a referenciar a nova lista gerada.

Funções com listas

Como vimos anteriormente, uma função é uma unidade de código de programa autônoma desenhada para cumprir uma tarefa particular, de modo a permitir a divisão de tarefas grandes em tarefas menores.

Problema 1:

Escreva uma função que recebe uma lista de inteiros e devolve o menor e o maior valor da lista.
def menorMaiorLista(lista):
    maior = lista[0]
    menor = lista[0]
    for x in lista:
        if x > maior:
            maior = x
        elif x < menor:
            menor = x
    return menor,maior
  
Essa função poderia ser testada no IDLE, por exemplo, com a seguinte chamada:
m,M = menorMaiorLista([5,-4,18,8,-9,4])
print("menor =",m,", maior=",M)
  

Problema 2:

Passsagem por referência: Exemplo de função que modifica uma lista e os efeitos persistem fora da função.
  1. Escreva uma função com protótipo

    def insereSeNovo(x, lista):

    que devolve o índice em que o real x ocorre em lista ou, caso x não estiver na lista, insere x no final da lista e devolve o índice dessa posição.


  2. Dados um número inteiro n e uma sequência de n números reais, escreva um programa (usando a função do item anterior) que conta e imprime quantas vezes cada número ocorre na sequência.

Solução 1:

def insereSeNovo(x, lista):
    i = 0
    achou = False
    while i < len(lista) and not achou:
        if lista[i] == x:
            achou = True
            ind = i
        i += 1
    if not achou:
        lista.append(x)
        ind = len(lista)-1
    return ind

# programa principal:
def main():
    n = int(input("Digite n: "))
    i = 0
    listaNum  = []
    listaCont = []
    while i < n:
        num = float(input("Digite num: "))
        ind = insereSeNovo(num, listaNum)
        if ind >= len(listaCont):
            listaCont.append(0)
        listaCont[ind] += 1
        i += 1

    i = 0
    while i < len(listaNum):
        print("%.2f aparece %d vezes"%(listaNum[i],listaCont[i]))
        i += 1

main()
    

Solução 2:

Uma solução mais compacta para a função insereSeNovo:

def insereSeNovo(x, lista):
    i = 0
    while i < len(lista):
        if lista[i] == x:
            return i
        i += 1
    lista.append(x)
    return len(lista)-1
  

Solução 3:

Uma solução alternativa para a função insereSeNovo que utiliza o comando if x not in lista:

def insereSeNovo(x, lista):
    if x not in lista:
        lista.append(x)
        ind = len(lista)-1
    else: # eu sei que x está na lista
        ind = 0
        while lista[ind] != x:
            ind += 1
    return ind
  

Problema 3:

  1. Escreva uma função que recebe um número inteiro e retorna uma lista contendo os seus dígitos. Os dígitos podem estar em ordem reversa.

  2. Escreva uma função que recebe uma lista e retorna uma lista contendo os seus elementos em ordem reversa.

  3. Escreva uma função que recebe 2 listas (l1 e l2) e devolve True caso:


    Caso contrário, a função deve devolver False.

  4. Escreva um programa que leia um número inteiro n > 0 e determina se ele é ou não palíndromo usando as funções anteriores. Um número inteiro é palíndromo se ele possui a mesma sequência de dígitos quando lido tanto da direita para a esquerda como da esquerda para a direita.

Solução 1:

def listaDeDigitos(n):
    lista = []
    while n > 0:
        dig = n%10
        lista.append(dig)
        n = n//10
    return lista


def ordemReversa(lista):
    inv = []
    i = len(lista)-1
    while i >= 0:
        inv.append(lista[i])
        i -= 1
    return inv


def iguais(l1, l2):
    if len(l1) != len(l2):
        return False
    i = 0
    while i < len(l1):
        if l1[i] != l2[i]:
            return False
        i += 1
    return True

# programa principal:
def main():
    n = int(input("Digite n: "))

    lista = listaDeDigitos(n)
    inv = ordemReversa(lista)

    if iguais(lista, inv):
        print(n,"é palíndromo")
    else:
        print(n,"não é palíndromo")

main()
    

Soluções alternativas:

Uma implementação alternativa para a função ordemReversa. Nessa versão usamos, no início do código, o comando [0]*n que gera uma lista com n elementos nulos.

def ordemReversa(lista):
    n = len(lista)
    inv = [0]*n
    i = 0
    while i < n:
        inv[i] = lista[n-1-i]
        i += 1
    return inv
  
Python permite a comparação de listas (ex: if l1 == l2:).
Logo, a função iguais poderia se resumir simplesmente a:
def iguais(l1, l2):
    return l1 == l2
  

Observações:

Python permite inverter listas (in place) usando lista.reverse(). Para criar uma cópia de uma lista a podemos usar b = list(a), ou usar b = a[:]
Logo, uma solução para o programa principal usando esses recursos disponíveis na linguagem poderia ser:

def main():
    n = int(input("Digite n: "))
    lista = listaDeDigitos(n)
    inv = list(lista)
    inv.reverse()

    if inv == lista:
        print(n,"é palíndromo")
    else:
        print(n,"não é palíndromo")

main()
  

Problema 4:

  1. Escreva uma função que recebe 2 inteiros ini e fim, e uma lista L, tal que 0 <= ini < fim <= len(L) e calcula a soma dos elementos L[i], para ini <= i < fim.

  2. Usando a função anterior, escreva um programa que leia um número inteiro n > 0 e uma sequência com n números reais, e determina um segmento de soma máxima. Um segmento é uma subsequência de números consecutivos, com pelo menos um elemento.

    Exemplo: Na sequência abaixo com n = 12
    5, 2, -2, -7, 3, 14, 10, -3, 9, -6, 4, 1,
    a soma do segmento de soma máxima é 3+14+10-3+9 = 33.

Solução 1:

def somasegmento(ini,fim,L):
    soma = 0.0
    i = ini
    while i < fim:
        soma += L[i]
        i += 1
    return soma

def main():
    n = int(input("Digite n: "))
    lista = []
    for i in range(n):
        num = float(input("Digite num: "))
        lista.append(num)

    smax = lista[0]
    imax,fmax = 0,1
    for i in range(n):
        for f in range(i+1,n+1):
            s = somasegmento(i,f,lista) 
            if s > smax:
                smax = s
                imax = i
                fmax = f

    print("Soma máxima =",lista[imax],end=" ")
    for i in range(imax+1,fmax):
        if(lista[i] < 0.0):
            print(lista[i],end=" ")
        else:
            print("+",lista[i],end=" ")
    print("=",smax)

main()
  

Solução 2:

Solução alternativa da função somasegmento usando o laço for.

def somasegmento(ini,fim,L):
    soma = 0.0
    for i in range(ini, fim):
        soma += L[i]
    return soma
  

Solução 3:

Solução compacta, aproveitando os recursos já existentes do Python de fatiamento de listas e soma dos valores de uma lista usando a função sum (exemplo, sum([3,-2,7]) gera o valor 8).

def somasegmento(ini,fim,L):
    return sum(L[ini:fim])