Funções
-
Exponenciação:
a) Escreva uma função que receba uma um número de ponto flutuante $a$ e um inteiro $b$ e devolva a potência $a^b$. Não use bibliotecas como
math
ou o operador de exponenciação**
.b) Use a função anterior e crie um programa que leia um númro $n$ do teclado e imprima todas as potências: $2^0, 2^2, \dots, 3^0, 3^1, \dots, n^{n}$.
c) Enquanto o primeiro item da questão pede uma função, o segundo item pede um programa. Via de regra, uma função deve se preocupar apenas com o subproblema sendo resolvido e não deve realizar leitura e impressão. Assim, ela deve receber a entrada via parâmetro(s) e devolver a saída como retorno(s). A sua função faz alguma chamada para
input()
ou paraprint()
? Explique porque isso seria uma má prática de programação. -
Escreva uma função que computa o fatorial de um número inteiro $n$ passado por parâmetro. Use a função anterior e crie um programa que imprima os valores de $n!$ para $n = 1, 2, \dots, 20$.
-
Escreva uma função que receba um número inteiro e devolva uma string com o mês correspondente ao número. Por exemplo, 2 corresponde a “fevereiro”. Use uma instrução
assert
no início da função para se certificar de que a entrada da função é válida. -
Escreva uma função que receba um número inteiro positivo menor ou igual a 50 e devolva uma string representando o número romano correspondente. Por exemplo, para entrada 5 a saída desejada é ‘V’. Utilize
assert
para verificar se a entrada é válida. -
Listas invertidas:
a) Faça uma função que receba uma lista e devolva uma nova lista com os elementos na ordem reversa. Não use a funções prontas, como
reversed()
ou similares.b) Faça uma função que receba uma lista e modifique essa lista, revertendo a ordem dos elementos. Não use a funções prontas, como método
reverse()
ou similares. Também, não utilize qualquer outra lista além da recebida como parâmetro. -
Primos:
a) Escreva uma função que decida se um número inteiro é primo. Se sim, deve devolver
True
, caso contrário,False
.b) Escreva uma função que recebe um número inteiro $n$ passado por parâmetro e devolve o maior número primo que é menor ou igual a $n$.
-
Casamento de padrões: dadas duas sequências de caracteres, uma chamada
texto
e outra chamadapadrao
, faça uma função que verifique se existe uma ocorrência depadrao
notexto
. Caso exista uma ocorrência, a função retorna a posição inicial do texto onde ocorre o padrão, caso contrário, retorna-1
. -
Crie uma função que calcule e devolva o número de arranjos de $n$ elementos $p$ a $p$. A fórmula do arranjo é a seguinte:
$$ A^n_p = \frac{n!}{(n-p)!} $$
Faça duas funções, uma que utiliza uma função fatorial como sub-rotina e uma que não utiliza. Para você, qual versão é melhor? Por quê?
-
Faça uma função que calcule a aproximação para a integral:
$$ \int_0^x e^{-u^2} = x - \frac{x^3}{3 \cdot 1!} + \frac{x^5}{5 \cdot 2!} - \frac{x^7}{7 \cdot 3!} + \dots $$
Sua função deverá ter a definição
def aprox_int(x, eps):
onde
x
é o limite da integral eeps
é um número real utilizado como critério de parada da aproximação de forma que sua função utiliza apenas termos cujo valor absoluto é pelo menoseps
. -
Notas:
a) Escreva uma função que leia do teclado o número de questões de uma prova e o valor de cada uma das questões. A função deve retornar a nota da prova.
b) Escreva uma função que receba como parâmetro um número de provas e leia do teclado o número de questões e os valores das questões de cada prova. A função deve retornar a média das provas.
c) Escreva um programa que leia do teclado um número de provas dadas em um semestre, o número de alunos matriculados e o número de questões e os valores das questões da cada prova de cada aluno. O programa deve imprimir a razão entre a média das notas dos alunos que tiraram pelo menos 5 e a média das notas dos alunos que tiraram abaixo de 5.
-
O resultado de uma função pode ser utilizado em qualquer expressão, inclusive como parâmetros para outras funções. Analise o código abaixo e faça um teste de mesa, i.e., simule usando papel. Qual o resultado impresso?
def soma(A, B): return A + B print(soma(1, soma(2, soma(3, 4))))
-
Em Python, é possivel nomear os parâmetros que estamos usando no momemento da chamada. Isso é útil para deixar explícito o que significa cada valor. Veja esse exemplo e indique a saída.
def dividir(dividendo, divisor): return dividendo / divisor print(dividir(3, 4)) print(dividir(divisor=3, dividendo=4))
-
Em vários momentos, precisamos retornar vários valores distintos. É possível fazer isso de várias maneiras. Uma delas é usando o que chamamos de tupla, veja o exemplo abaixo. Podemos devolver qualquer tipo de estrutura.
def funcao(): texto = "dois" numero = 2 return texto, numero tupla = funcao() print(tupla[1]) texto, numero = funcao() print(texto)
Faça uma função que leia uma lista de $n$ inteiros (com repetição) e retorne uma tupla $(a, m)$, em que $a$ é o número com maior número de repetições e $m$, o número de repetições desse elemento.
-
Produtos de ímpares:
a) Escreva uma função que decida se um número é produto de dois números ímpares. Se for, a função deverá devolver esses dois números, do contrário, deverá devolver
None
.b) Escreva uma função que decida se um número é produto de quatro números ímpares. Se for, a função deverá devolver esses quatro números, do contrário, deverá devolver
None
. Tente utilizar a função anterior. -
Em Python, uma função é uma variável e pode ser inclusive passada como parâmetro. Veja o exemplo abaixo:
def media_aritmetica(a, b): return (a + b) / 2 def calcular_medias(provas, exercicios, funcao_media): finais = [] n = len(provas) for i in len(n): nota_final = funcao_media(provas[i], exercicios[i]) finais.append(nota_final) return finais def main(): provas = [3.5, 7.5, 10] exercicios = [7.0, 6.0, 9.8] finais = calcular_medias(provas, exercicios, media_aritmetica) print(finais)
Modifique o programa, de forma a utilizar a média geométrica, ao invés da média aritmética.
-
Escreva uma função chamada
raiz
que recebe um intervalo $[a,b]$ e uma função real $f$ passada por parâmetro. A funçãoraiz
deve devolver uma aproximação da raiz da a equação $f(x) = 0$. Essa função passada recebe um número de ponto flutuante e devolve outro número. Suponha que a função que ela calcula é contínua e que existe uma única raiz para nesse intervalo. Uma aproximação da raiz é qualquer número $x’$ tal que $|x - x’| < \epsilon$ para $\epsilon$ escolhido (digamos, $10^{-6}$).
Escopo e visibilidade
-
Lembre-se de sempre verificar se suas funções estão devolvendo algum valor. O que o código abaixo imprime? Verifique sua resposta com um computador e justifique a saída. Experimente fazer modificações.
a = 1 b = 2 c = 0 def soma1(a, b): c = a + b def soma2(a, b): return a + b res1 = soma1(a, b) res2 = soma2(a, b) print(f"c = {c}") print(f"res1 = {res1}") print(f"res2 = {res2}")
-
É importante entender como funções modificam listas e outras estruturas passados por parâmetro. Veja o código abaixo e determine o que é impresso. Depois, verifique no computador. O resultado foi diferente do que você esperava? Explique.
def substituir_variavel(variavel): variavel = 4 def substituir_lista(lista): lista = [5, 5, 5, 5] def modificar_lista(lista): lista.append(6) variavel = 0 lista = [1, 2, 3] substituir_variavel(variavel) substituir_lista(lista) modificar_lista(lista) print(f"variavel = {variavel}") print(f"lista = {lista}")
-
Considere o código abaixo:
lista = [1, 2, 3] c = 2 def main(): soma = 0 produto = 1 for i in lista: soma = somar(soma, i) produto = multiplicar(produto, i) print(f"soma = {soma}, produto = {produto}") def somar(a, b): c = 3 return a + b + c def multiplicar(a, b): return a * b * c main()
a) Determine quais são as variáveis locais e globais deste programa, identificando a que função pertence cada variável local.
b) Mostre o que será impresso na tela quando este programa for executado. Depois verifique seu programa com um computador.
-
Para controlar quais funções estão sendo chamadas e quais variáveis estão sendo modificadas, devemos estudar a visibilidade de variáveis, isso é, o que cada uma das funções “enxerga”. Isso é particularmente importante quando trabalhamos com vários arquivos. Para isso, temos alguns modificadores, que são palavras chaves especiais como
global
(na prática, nunca devemos usar essa palavra-chave). Fazer declarações de funções e variáveis com esses modificadores pode implicar em maneiras diferentes da organização da memória.Arquivo
lib.py
:a = 21 def f1(): print(a) global c c = 77; print(c) return def f3(): f2(1,2) print(c) return def f2(a, b): print(a + b) f1() print(c) return def f4(): c = 14 print(c) f3() print(c) return
Arquivo
main.py
:import lib def main(): e = 3 lib.f4() print(lib.c) return main()
a) Para cada função, descreva as variáveis visíveis e funções visíveis. Diga quais variáveis são locais ou não.
b) Faça um desenho da organização da memória no momento em que a função
f2
estiver executando.c) Como você faria para contar o número de vezes que uma função é chamada em um programa?
Pythonismo
Python tem algumas convenções, funções e sintaxes particulares que outras linguagens não possuem, ou que são implementadas de maneira diferente. Assim, é comum entrar em fóruns online e ver discussões sobre códigos “pytônicos”, isto é, códigos que foram otimizados para legibilidade e funcionamento com Python. Nesta disciplina, queremos aprender algoritmos, então às vezes evitamos construções como essas, mas nesta seção vamos tentar implementar coisas de jeitos pytônicos.
-
Considere uma variável
lista
. Qual a diferença entrefor valor in lista
efor i in range(len(lista))
? Explique com qual a diferença das variáveis de iteraçãovalor
ei
nos doisfor
s. -
Quando estamos trabalhando com listas, é normal utilizarmos list comprehensions para facilitar o trabalho, particularmente quando queremos filtrar um subconjunto de elementos.
a) O que o código abaixo imprime?
A = [-2, 3, -5, 1, 0, - 11] B = [i for i in A if i < 0] print(B)
b) Baseando-se no exemplo acima, cria uma list comprehension que copia em B os elementos múltiplos do primeiro elemento da lista A. Preencha o modelo:
A = [5, 8, 3, 0, 15, 7, 20, 13, 30, 31] B = ... print(B)
Depois, verifique com o auxílio de um computador.
c) Se a condição de filtragem for mais elaborada, podemos criar funções para deixar a list comprehension mais simples. O quê o código abaixo imprime?
def eh_soma_grande(c): if c + 3 >= 8: return True else: return False a = [2, 8, 3, 11, 4, 5, 6, 7] b = [i for i in a if eh_soma_grande(i)] print(b)
d) Baseando-se no exemplo acima, cria uma list comprehension que copia em B os elementos da lista A que são primos.
-
Na maioria das vezes que queremos percorrer uma lista, estamos interessados no valor dos elementos, mas algumas vezes também estamos interessados nos índices em que esses elementos estão. Para percorrer tanto índices, quanto valores, usamos
enumerate()
. A função abaixo devolve tanto a posição do maior elemento quanto seu valor.def maior(lista): maior = lista[0] pos_maior = 0 for i, valor in enumerate(lista): if valor > maior: maior = valor pos_maior = i return i, maior
a) A função acima faz uma comparação a mais do que o necessário. Como você pode alterar o código para evitar essa comparação?
b) Crie uma função que receba uma palavra e devolva uma lista de todos as letras que aparecem nessa palavra imediatamente antes de “p” e “b”. Tente utilizar
enumerate()
. -
Funções dentro de funções é algo que você vai encontrar em em diversos códigos em Python. As nested functions, ou funções aninhadas, são utilizadas por uma série de razões.
a) Quando um trecho de código é repetido várias vezes dentro de uma função, podemos criar uma função interna para evitar repetição de código.
def operacoes(a, b): def verificar(x, y, op, res): if res == 42: print(f"O valor de {x} {op} {y} é {res}.") print("Essa é uma operação fundamental!") verificar(a, b, '+', a + b) verificar(a, b, '-', a - b) verificar(a, b, '*', a * b) verificar(a, b, '/', a / b) operacoes(6, 7)
Qual a saída desse programa? O que acontece se tentarmos chamar
verificar
a partir da função principal? Experimente com um computador.b) A função interna pode acessar as variáveis do escopo em que está definida. Nesse caso, chamamos essa função de
clousure
, pois ela mantém junto dela referência a algumas das variáves da função.def operacoes(a, b): fundamentais = [] def verificar(op, res): if res == 42: print(f"O valor de {a} {op} {b} é {res}.") fundamentais.append(op) verificar("+", a + b) verificar("-", a - b) verificar("*", a * b) verificar("/", a / b) print(f"As operações fundamentais são {fundamentais}.") operacoes(6, 7)
Compare este trecho com o trecho anterior. Qual a diferença? Qual a saída do programa?
-
Por vezes, queremos iterar sobre duas listas simultaneamente. Para esse fim, existe a função
zip()
, que cria um iterador que agrega duas ou mais sequências (listas, tuplas, etc…). Veja um exemplo:lista_a = [1, 2, 3, 4, 5] lista_b = ['a', 'b', 'c', 'd', 'e'] for a, b in zip(lista_a, lista_b): print(a, b)
Essa função irá imprimir
1 a
,2 b
etc. Utilizezip
para receber três listasa
,b
,c
, com valores inteiros e criar uma listad
de modo qued[i] == a[i] * b[i] * c[i]
. Depois, descubra o que acontece quando as listas não possuem o mesmo tamanho. -
Em Python, erros normalmente são tratados por meio das chamadas
exceções
, que representam condições de erro ou que impedem o fluxo normal de execução. Vamos nos basear em um programadivisao.py
que recebe dois números e imprime a divisão.def dividir(a, b): return a / b def main() print("Este programa divide a por b") a = int(input("Digite o valor de a: ")) b = int(input("Digite o valor de b: ")) print(f"O resultado da divisão é {dividir(a, b)}.") main()
Podemos testar com algumas entradas.
user@desktop:~/$ python3 divisao.py Este programa divide a por b Digite o valor de a: 1 Digite o valor de b: 2 O resultado da divisão é 0.5. user@desktop:~/$ python3 divisao.py Este programa divide a por b Digite o valor de a: 1 Digite o valor de b: 0 Traceback (most recent call last): File "divisao.py", line 13, in <module> main() File "divisao.py", line 11, in main print(f"O resultado da divisão é {dividir(a, b)}.") File "divisao.py", line 2, in dividir return a / b ZeroDivisionError: division by zero
Perceba que na segunda divisão, ao tentarmos dividir por zero, o programa termina com uma mensagem de erro. Essa mensagem de erro corresponde a uma condição de exceção não tratada
ZeroDivisionError
.Isso pode indicar um bug no programa, mas também pode acontecer que algumas condições de exceção sejam esperadas. Por exemplo, enquanto não podemos dividir por zero, pode ser que queiramos que o usuário possa digitar qualquer número como divisor.
Já que não há como continuar a execução normal dividindo por zero, temos que instruir o interpretador Python a o que fazer quando uma condição como essa acontecer. O que queremos fazer é tratar essa exceção para que o programa continue rodando.
Para isso, podemos usar as construções
try
e oexcept
. No bloco detry
testamos um bloco de código no fluxo normal de execução. Se ocorrer alguma exceção, o fluxo de execução será desviado para o bloco deexcept
correspondente; nesse bloco, devemos tratar o erro. Por exemplo, adaptando o código acima para tratar a divisão por 0, temos o código a seguir.def dividir(a, b): return a / b def main(): print("Este programa divide a por b") a = int(input("Digite o valor de a: ")) b = int(input("Digite o valor de b: ")) try: print(f"O resultado da divisão é {dividir(a, b)}.") except ZeroDivisionError: print("Não é possível dividir por zero.") main()
Dessa vez, a execução do programa continua com o tratamento da exceção.
user@desktop:~/$ python3 divisao.py Este programa divide a por b Digite o valor de a: 1 Digite o valor de b: 0 Não é possível dividir por zero.
Observe que o nome do erro é colocado após a palavra-chave
except
. Essa palavra-chave indica qual exceção estamos esperando; pode haver vários blocos. Portanto, para qualquer tipo de erro,ZeroDivisionError
,EOFError
, entre outros, podemos tratá-lo para o nosso programa continuar executando ao nosso critério.Escreva uma função que leia do teclado um número de ponto flutuante. Para converter uma string em número, use a função
float
. Se a linha digitada não for uma string que possa ser convertida em ponto flutuante, a função deve tentar novamente até que um valor válido seja digitado.
Exercícios criativos
-
Modificando o comportamento de funções:
a) Escreva uma função
numero_palavras
que conta o número de palavras que terminam com “s” em um texto dado.b) Escreva uma função
eh_plural
que devolveTrue
se uma palavra termina coms
eFalse
, caso contrário.c) Reescreva uma função
numero_palavras
utilizando a funçãoeh_plural
como sub-rotina. Você pode redefinir a assinatura da primeira função para torná-la mais conveniente.d) Suponha agora que você esteja interessado em contar outros tipos de palavras de interesse, digamos, palavras que terminal com
ão
, ou palavras que começam coma
. Reescreva a funçãonumero_palavras
para que ela conte as palavras de interesse. As palavras de interesse são identificadas por uma função indicadora passada por parâmetro, isso é, uma função que recebe uma palavra e devolveTrue
se for de interesse eFalse
caso contrário.