x
MAC2166 - Introdução à Computação
x
29/04/2014 - Aula 9
x
Copiando listas
x
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:
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]
x
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:
num1 = 20
num2 = num1
print(num1, num2)
num1 = 30
print(num1, num2)
num2 = 50
print(num1, num2)
20 20 30 20 30 50
x
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()
criada no Problema 18 exemplifica uma forma de se fazer isso.
x
Problema 18 (Jogo Senha)
Faça um programa que sorteie aleatoriamente um número inteiro (a senha) e depois ajude o usuário a adivinhar qual é o valor da senha. O seu programa deve:
(1) Receber um número inteiro semente
que será a semente
do gerador de números aleatórios (veja mais sobre a geração de números
aleatórios mais a frente, nesta mesma página).
(2) Sortear um número aleatório, maior ou igual a zero, de no máximo 5 dígitos, que será a senha
.
(3) Receber um número inteiro chute
, maior ou igual a zero, de no máximo 5 dígitos que será uma tentativa de adivinhar a senha
.
(4) Imprimir na tela:
- número de dígitos em posições certas e
- número de dígitos certos da tentativa.
Por exemplo, se senha = 74894
e chute = 45897
, os dígitos 8
e 9
do chute estão nas posições corretas, e os dígitos 4
e 7
do chute estão corretos mas em posições incorretas. Portanto, para o
chute 45897 há 4 dígitos certos e 2 deles em posições certas.
Embora os números sejam digitados sempre com 5 ou menos dígitos, todos os dígitos à esquerda, até completar 5 dígitos, são sempre considerados zeros. Por exemplo, se o número sorteado é 10 e o número chutado é 1, considera-se que o número sorteado é 00010 e o número chutado é 00001. Com isso, o número de dígitos certos é 5 e o número de dígitos na posição certa é 3, que são os 3 zeros à esquerda.
(5) Repetir os passos 3 e 4 enquanto a senha não for descoberta e o número de tentativas não ultrapassar 10.
Obs.: Este problema é parte do EP2 das outras turmas de MAC2166, que por sua vez foi inspirado pelo jogo Mastermind, chamado no Brasil de Senha. Você pode ver soluções para esse EP que não envolvem o uso de listas aqui.
"""
Jogo Senha (Mastermind)
"""
import random
## definição de alguma constantes
MAX_TENTATIVAS = 10
NUM_DIGITOS = 5
MAX_NUMERO = 99999
MODO_DEBUG = True
##############################################################################
def main():
'''
Função que implementa o jogo Senha (ou Mastermind)
'''
# entrada dos dados
print("=========================================================================")
semente = int(input("Digite a semente para inicializar o gerador de números aleatórios: "))
random.seed(semente)
# gera aleatoriamente a senha, um número entre 0 e MAX_NUMERO
senha = random.randint(0, MAX_NUMERO)
# cria uma lista contendo os dígitos da senha
senha_lista = para_lista(senha, NUM_DIGITOS)
# mensagens
print("=========================================================================")
print("Bem-vindo ao jogo! Qual é a senha?")
print("=========================================================================")
if MODO_DEBUG: ### MUDE A CONSTANTE MODO_DEBUG PARA True OU False
print("MODO_DEBUG: o número senha foi %d"%(senha))
##############################################################################
# malha principal do Mastermind
tentativa = 0
acertou = False
while tentativa < MAX_TENTATIVAS and not acertou:
tentativa += 1
chute = int(input("Tentativa %d .....: "%tentativa))
chute_lista = para_lista(chute, NUM_DIGITOS)
emparelhados = posicoes_certas(chute_lista, senha_lista)
certos = digitos_certos(chute_lista, senha_lista)
print("Número de dígitos em posições certas: ", emparelhados)
print("Número de dígitos certos: ", certos)
acertou = emparelhados == NUM_DIGITOS
###########################################################################
# FINALIZANDO
print("=========================================================================")
if acertou:
print("Parabéns, você acertou em %d tentativas!"%(tentativa))
else:
print("Que pena, você errou.")
print("=========================================================================")
###########################################################################
# Função para_lista
def para_lista_inv(num, n):
""" (int, int) --> list
para_lista recebe inteiros num e n, e cria e retorna uma lista com
seus n dígitos menos significativos.
Essa versão retorna uma lista com os dígitos invertidos (eles aparecem
invertidos usando print).
"""
lista = []
for i in range(n):
lista.append(num % 10)
num //= 10
return lista
def para_lista(num, n):
""" (int, int) --> list
essa versão retorna os dígitos na mesma ordem.
"""
lista = [0] * n
for i in range(n):
lista[n -1 -i] = num % 10
num //= 10
return lista
###########################################################################
# Função posicoes_certas
def posicoes_certas(num1, num2):
"""(list, list) --> int
recebe duas listas e retorna o número dígitos em "posições certas".
Exemplos:
>>> posicoes_certas([1, 2, 3, 2, 1],[0, 1, 2, 2, 1])
2
>>> posicoes_certas([1, 2, 3, 2, 1],[2, 1, 2, 1, 3])
0
"""
cont = 0
for i in range(len(num1)):
if num1[i] == num2[i]:
cont += 1
return cont
###########################################################################
# Função digitos_certos
def digitos_certos_versao_errada(num1, num2):
""" (list, list) --> int
recebe duas lista e retorna o número de "dígitos certos"
Essa versao funciona para a 1a tentativa apenas, pois destroi num2.
"""
cont = 0
for i in range(len(num1)):
j = 0
achei = False
while j<len(num2) and not achei:
if num1[i] == num2[j]:
cont+=1
num2[j] = -1
achei = True
j+=1
return cont
def digitos_certos(num1, num2):
""" (list, list) --> int
recebe duas lista e retorna o número de "dígitos certos"
versao com clone e destroi o clone
"""
copia_num2 = clone(num2)
cont = 0
for i in range(len(num1)):
j = 0
achei = False
while j<len(copia_num2) and not achei:
if num1[i] == copia_num2[j]:
cont+=1
copia_num2[j] = -1
achei = True
j+=1
return cont
def digitos_certos_com_flags(num1, num2):
""" (list, list) --> int
recebe duas listas e retorna o número de "dígitos certos"
versao com flags
"""
marcado = [False] * len(num2)
cont = 0
for i in range(len(num1)):
j = 0
achei = False
while j<len(num2) and not achei:
if not marcado[j] and num1[i] == num2[j]:
cont+=1
marcado[j] = True
achei = True
j+=1
return cont
###########################################################################
def clone(lista):
""" (list) --> list
recebe uma lista e cria e retorna um clone
Dá para fazer return lista[:] (esse slice cria uma copia da lista inteira)
"""
clone = []
for i in range(len(lista)):
clone.append(lista[i])
return clone
###########################################################################
main()
x
No solução acima, temos uma função chamada main()
,
que contém todo o código do programa principal. A função só foi chamada
no final do código (mais precisamente, na última linha).
Note que a função main()
, dentro dela, chama outras funções que só são definidas mais para baixo no código, e isso não é um problema!
Assim, ao criar uma função main()
e colocar dentro dela
todo o código do programa principal, não precisamos nos preocupar em
colocar a definição das funções chamadas no programa principal antes
dele no código, precisamos apenas fazer a chamada à função main()
depois das definições das funções usadas nela.
x
Sobre a geração de números (pseudo-)aleatórios
[Texto extraído do enunciado do EP2]
Para gerar números aleatórios, precisamos usar algumas funções
prontas no Python. Para usar essas funções, é necessário carregar o
módulo random
onde elas estão implementadas. Para isso, a linha abaixo deve ser incluída no início do seu código:
import random
Com o módulo carregado, é necessário inicializar o gerador de números
aleatórios com uma semente, que vai ser um dos valores que o usuário
terá que passar como entrada no Problema 18. Supondo que a semente
digitada pelo usuário seja armazenada na variável semente
, a linha abaixo inicializa o gerador de números aleatórios com ela:
random.seed(semente)
O uso de semente permite que o programa seja executado sempre com o mesmo número pseudo-aleatório como senha, facilitando portanto o teste e correção do programa. Na prática, se você quisermos deixar a senha mais aleatória, basta não passar uma semente explicitamente pois o Python utilizá a hora do computador como semente. Como o instante em que o programa é executado é, de certa forma, aleatório, a senha se torna difícil de ser prevista.
Para entender melhor o que são e como números "aleatórios" (ou quase) são gerados no computador, leia esta página.
Uma vez inicializada a geração dos números aleatórios, o código abaixo mostra como sortear um número inteiro maior ou igual a 1 e menor ou igual a 99 e atribuir o resultado do sorteio à variável sorteio:
sorteio = random.randint(1,99)
x
Problema Extra 1
(a) Escreva uma função com protótipo
def insereSeNovo (x, lista):
que devolve a posição 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.
(b) Dada uma sequência de n
números reais, escreva um programa (usando a função do item anterior) que conte quantas vezes cada número ocorre na sequência.
###
# programa principal
###
def main()
n = int(input("Digite n: "))
cont = 0
listaNum = []
listaCont = []
while cont < n:
num = float(input("Digite um número: "))
ind = insereSeNovo(num, listaNum)
if ind > len(listaCont)-1:
listaCont.append(1)
else:
listaCont[ind] += 1
cont += 1
cont = 0
while cont < len(listaNum):
print ("O num %f aparece %d vezes"%(listaNum[cont], listaCont[cont]))
cont += 1
def insereSeNovo (x, lista):
ind = -1
cont = 0
# procura
while cont < len(lista) and ind == -1:
if (lista[cont] == x):
ind = cont
cont += 1
# se não achei
if ind == -1:
lista.append(x)
ind = len(lista) - 1
return ind
def insereSeNovo1 (x, lista):
if x not in lista:
lista.append(x)
return len(lista) - 1
else:
# eu sei que x está na lista
ind = 0
while lista[ind] != x:
ind += 1
return ind
def insereSeNovo2 (x, lista):
for i in range(len(lista)):
if x == lista[i]:
return i
lista.append(x)
return len(lista)-1
def insereSeNovo3 (x, lista):
if x not in lista:
lista.append(x)
i = 0
while x != lista[i]:
i += 1
return i
def insereSeNovo4 (x, lista): # melhor solucao estilo C
ind = 0
while ind < len(lista) and lista[ind] != x :
ind += 1
if ind == len(lista) :
lista.append(x)
return ind
def insereSeNovo5 (x, lista): # solucao totalmente pythonica
if not x in lista :
lista.append(x)
return lista.index(x)
####################
main()
x
Problema Extra 2
(a) 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
.
(b) Usando a função anterior, escreva um programa que leia n>0
e uma sequência com n
números reais, e determina um segmento de soma máxima.
Exemplo: Na sequência
5, 2, -2, -7,
3, 14, 10, -3, 9,
-6, 4, 1
,
a soma do segmento de soma máxima é 33.
# Programa Principal
def main():
# Lê a sequência de valores e armazena-os em uma lista
n = int(input("Digite n: "))
seq = []
for i in range(n):
num = float(input("Digite um número da sequência: "))
seq.append(num)
max = seq[0] # max não pode ser inicializada com 0,
# porque a lista pode ser toda negativa
ini = 0
while ini < n:
fim = ini
while fim < n:
s = soma(ini, fim, seq)
# alternativa para a linha anterior: s = sum(seq[ini:fim+1])
if s > max:
max = s
fim += 1
ini += 1
print("A soma máxima de um segmento da sequência é %d." %max)
# Função soma (solução 1)
def soma(ini, fim, lst):
s = lst[ini]
while ini < fim:
ini += 1
s += lst[ini]
return s
# Função soma (solução 2)
def soma1(ini, fim, lst):
s = 0
for x in lst[ini:fim+1]:
s += x
return s
##################################
main()
x
Tópicos vistos na Aula 9
- Funções que recebem listas como parâmetros ou que devolvem listas
x
Referências e outros materiais para estudo
- Computer Science Circles >> http://cscircles.cemc.uwaterloo.ca/
- Think Python - How to Think Like a Computer Scientist >> http://www.greenteapress.com/thinkpython/thinkpython.html
- Projeto MacMulti - Listas de exercícios (Introdução à Computação) >> http://www.ime.usp.br/~macmulti/exercicios/