import numpy as np
import time6 Funções
6.1 Introdução
Até aqui, os programas escritos consistem em sequências de instruções, condicionais e laços que manipulam objetos como números, listas e dicionários. Essa forma é suficiente para resolver problemas simples, mas rapidamente se torna difícil de manter quando o código cresce ou quando a mesma lógica precisa ser reutilizada várias vezes.
Funções são o principal mecanismo de abstração em programação. Elas permitem encapsular um conjunto de instruções sob um nome, definir claramente quais informações entram e quais resultados saem, e separar a lógica interna da interface externa. Em termos computacionais, funções organizam o fluxo de execução, delimitam escopos de variáveis e tornam explícita a transformação entre inputs e outputs – conceito central também em Economia, onde frequentemente modelamos relações funcionais entre variáveis.
Neste capítulo, o objetivo é compreender o papel das funções como ferramenta de organização, reutilização e clareza lógica. Ao final, o aluno deverá ser capaz de estruturar programas de forma modular e compreender como funções são usadas extensivamente nas bibliotecas científicas que serão estudadas nas próximas aulas.
6.1.1 Pré-requisitos
Para trabalhar com funções e todas as suas funcionalidades, utilizaremos dois pacotes externos:
numpytime
6.2 O que é uma função?
No contexto das linguagens de programação, uma função é uma sequência nomeada de instruções, que executa algum tipo de operação específica mas não necessariamente numérica, como é o caso das funções matemáticas. A ideia essencial por trás de uma função é a de juntar algumas tarefas comuns ou repetidas e criar uma função para que, em vez de escrever o mesmo código várias vezes, possamos chama-la pelo nome e reutilizar o conjunto de instruções nela contida sempre que necessário.
Caso o objetivo de dividir um programa em funções ainda não tenha ficado claro, saiba que:
Criar uma nova função dá a oportunidade de nomear um grupo de instruções, o que deixa o seu programa mais fácil de ler e de depurar
As funções podem tornar um programa menor, eliminando o código repetitivo. Depois, caso precise fazer alguma alteração, basta fazê-la em um lugar só.
Dividir um programa longo em funções permite depurar as partes uma de cada vez e então reuni-las em um conjunto funcional.
As funções bem projetadas muitas vezes são úteis para muitos programas. Uma vez escritas e depuradas, você pode reutilizar as funções em programas fora daquele para o qual elas foram originalmente construídas.
Existem várias funções nativas no Python (e.g., type() para encontrar o tipo de um objeto) e mesmo dentro de bibliotecas com as quais já trabalhamos um pouco (e.g., numpy.random.uniform() para sortear números aleatórios de acordo com uma distribuição uniforme). À partir de agora veremos como podemos criar nossas próprias funções dentro da linguagem!
No Python, a sintaxe de uma função é dada por:
def nome_da_funcao(argumentos):
<instruções> Assim como nos loops, para definir uma nova função no Python é preciso começar com uma palavra-chave, nesse caso def. O nome que daremos à função vem logo em seguida. É através desse nome, nome_da_funcao, que chamaremos essa função em outras partes do nosso código. Entre parênteses definimos os argumentos que a função recebe para realizar o conjunto de instruções em <instruções>.
Note, mais uma vez, que todo o bloco de código que estiver identado e abaixo da linha de cabeçalho da função fará parte da função. Assim como nos loops, a função termina quando passarmos a primeira linha de código não-identada.
6.3 Nossa primeira função
Vamos começar criando uma função simples, que tem por objetivo printar uma das frases mais conhecidas da história do cinema:
def frase_cinema():
print('Que a força esteja com você!')Note que nesse caso, o parênteses logo após o nome da função está vazio. Isso quer dizer que essa função não recebe argumentos, apenas printa a frase entre aspas sempre que for chamada. Para chamá-la, basta usar o nome frase_cinema seguido dos parênteses vazios:
frase_cinema()Que a força esteja com você!
6.4 Argumentos de uma função
Na função anterior, não tínhamos nenhum argumento. Ou seja, toda vez que você chamar a função frase_cinema, ela vai fazer a mesma coisa. Pode até ser que seja o seu objetivo fazer exatamente isso – é uma forma de economizar código.
O ponto é que a ideia de função é muito mais poderosa do que simplesmente uma “abreviação” de um monte de linhas de código. A abstração implícita no conceito de função é poderosa o suficiente para garantir que a função retorne coisas diferentes caso você altere algum argumento. Vamos falar disso a seguir.
6.4.1 Keyword arguments e default values
Vamos então criar uma função que tenha argumentos.
def my_func(name,place):
print(f"Olá {name}! Você é de {place}?")
my_func("Emily","Paris")Olá Emily! Você é de Paris?
O que acontece se você especificar o place primeiro e depois o name? Vamos descobrir.
my_func("Hawaii","Robert")Olá Hawaii! Você é de Robert?
Meio bizarro, não? A razão é que aqui temos os chamados argumentos posicionais. Ou seja, a função vai assumir que o primeiro argumento é o name e o segundo é o place não importa o que tenhamos passado como argumento. Para lidar com isso, a gente pode atribuir um nome a cada um dos argumentos, ou palavra-chave, que aí vai ser a palavra-chave, e não a posição, que vai determinar qual o valor atribuído a cada argumento.
my_func(place="Hawaii",name="Robert")Olá Robert! Você é de Hawaii?
Vimos aqui que a posição passou a ser irrelevante porque colocamos os nomes de cada um dos argumentos. E se quiséssemos dar mais flexibilidade ainda à função, só especificando um subconjunto dos argumentos? Para que isso funcione, nós precisamos especificar os valores-padrão dos argumentos caso eles não sejam fornecidos.
Aqui tem uma função que faz isso.
def total_calc(bill_amount,tip_perc=10):
total = bill_amount*(1 + tip_perc/100)
total = round(total,2)
print(f"Please pay ${total}")Nesse caso, o argumento bill_amount é obrigatório. Por outro lado, o argumento tip_perc vai assumir o valor de 10 toda vez que ele não for explicitamente fornecido na chamada da função.
6.4.2 Argumentos arbitrários
Vamos começar fazendo algumas perguntas: * E se não soubermos o número exato de argumentos de antemão? * Podemos criar funções que funcionem com um número variável de argumentos?
A resposta é sim! E vamos criar essa função imediatamente. Vamos criar uma função simples my_var_sum() que retorna a soma de todos os números passados como argumento. No entanto, o número de argumentos pode ser potencialmente diferente cada vez que chamamos a função.
def my_var_sum(*args):
sum = 0
for arg in args:
sum += arg
print(f"The numbers that you have add up to {sum}")Observe como a definição da função agora tem *args em vez de apenas o nome do parâmetro. No corpo da função, fazemos um loop em args até usarmos todos os argumentos. A função my_var_sum retorna a soma de todos os números passados como argumentos. Olha só o que acontece quando chamamos a função com diferentes números de argumentos:
my_var_sum(99,10,54,23)
my_var_sum(9,87)
my_var_sum(5,21,36,79,45,65)
my_var_sum(1)The numbers that you have add up to 186
The numbers that you have add up to 96
The numbers that you have add up to 251
The numbers that you have add up to 1
Mas e se eu quiser passar não apenas uma sequência de valores, mas uma sequência de valores com nomes? Não se preocupe, existe uma possibilidade de fazer isso, usando o **kwargs na definição dos argumentos. Qual é a diferença entre *args e **kwargs? A diferença é que você vai passar uma sequência de tamanho arbitrário de parâmetros, cada um deles nomeado.
O Python vai entender o **kwargs como um dicionário. Cada elemento passado é um par chave-valor, e dentro da função você tem que desempacotar o valor da chave. Vamos seguir alguns exemplos.
def myFun(**kwargs):
for key, value in kwargs.items():
print("%s == %s" % (key, value))
myFun(first='Geeks', mid='for', last='Geeks')first == Geeks
mid == for
last == Geeks
Podemos misturar os tipos de argumentos (posicionais versus *args/*kwargs)? Sim, como podemos ver no exemplo seguinte:
def myFun(arg1, **kwargs):
for key, value in kwargs.items():
print(arg1+" %s == %s" % (key, value))
myFun("Hi - ", first='Geeks', mid='for', last='Geeks')Hi - first == Geeks
Hi - mid == for
Hi - last == Geeks
6.4.3 Elementos dentro da função: execução condicional
As funções podem retornar objetos booleanos também, o que pode ser conveniente para esconder testes complicados dentro de funções. Por exemplo:
def is_divisible(x, y):
if x % y == 0:
print(True)
else:
print(False)
is_divisible(6, 4)False
6.4.4 Aplicação: Teorema Central do Limite - Parte 1
O primeiro passo para replicarmos, na forma de uma função, a aplicação do Teorema Central do Limite da aula anterior é definir uma função que recebe como argumentos a distribuição da varíavel aleatória X, o intervalo no qual a variável está definida e o número de sorteios que faremos em cada amostra. Com o que aprendemos até agora podemos definir a função func_tcl abaixo:
def func_tcl(dist=None,intervalo=(0,1),n=100):
if dist == None:
print('Você esqueceu de carregar uma função que defina a distribuição de X! Volte 2 casas.')
else:
x = dist(intervalo[0],intervalo[1],n)
print(x)Para todos os 3 argumentos definimos valores default que, caso não sejam alterados a função vai utilizá-los em suas operações. Vamos rodar a função com todos os valores default e ver o que acontece:
func_tcl()Você esqueceu de carregar uma função que defina a distribuição de X! Volte 2 casas.
Nesse caso, a execução condicional if dentro da função acende já que não passamos nenhuma distribuição como default, apenas o valor None. Vamos então repetir o que fizemos na aula passada e utilizar a distribuição uniforme, no intervalo \(\left[-40,40\right]\) e sorteando apenas 10 números desta distribuição:
func_tcl(dist=np.random.uniform,intervalo=(-40,40),n=10)[ 3.28146467 -30.7740039 11.65928446 -24.55760713 -36.95478501
20.89774271 13.91643735 -11.19203137 -39.6549853 21.21748996]
6.4.5 Elementos dentro da função: iteração
Da mesma forma que podemos ter condições lógicas dentro da função, podemos ter blocos de repetição (for e while) dentro da função, assim como no caso de *args. Vamos olhar para um outro exemplo em que a função recebe uma lista de strings como argumento e faz operações em cada um dos elementos, um a um:
states = [' Alabama ','Georgia!','Georgia','georgia','FlOrIda','south carolina##','West virginia?']
def clean_strings(lista_strings):
result=[]
for value in lista_strings:
value = value.strip()
value = value.title()
value = value.replace('#','')
value = value.replace('?','')
value = value.replace('!','')
result.append(value)
print(result)
print(states)
print()
clean_strings(states)[' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda', 'south carolina##', 'West virginia?']
['Alabama', 'Georgia', 'Georgia', 'Georgia', 'Florida', 'South Carolina', 'West Virginia']
6.4.6 Aplicação: Teorema Central do Limite - Parte 2
Agora podemos dar mais um passo e incorporar o loop dentro da função, passando como novo argumento da função o número de amostras com o qual queremos trabalhar:
def func_tcl(dist=None,intervalo=(0,1),n=100, samples=10):
if dist == None:
print('Você esqueceu de carregar uma função que defina a distribuição de X!')
else:
means = []
for j in range(0,samples):
x_func_tcl = dist(intervalo[0],intervalo[1],n)
mean_x = sum(x_func_tcl)/len(x_func_tcl)
means.append(mean_x)
print('Essa é a lista de médias:')
print(means)
print('\nE essa é a média das médias:')
print(sum(means)/len(means))# utilizamos a função seed mais uma vez para tornar os resultados previsíveis
np.random.seed(1)
func_tcl(dist=np.random.uniform,intervalo=(-40,40),n=50,samples=10)Essa é a lista de médias:
[np.float64(-2.6419520291841945), np.float64(0.382420445207494), np.float64(-2.2971869003899186), np.float64(-0.12856511296217676), np.float64(3.945331018819081), np.float64(4.808427607983528), np.float64(1.1596981846490986), np.float64(0.4583673137720098), np.float64(-2.0177969126899638), np.float64(2.952952578448611)]
E essa é a média das médias:
0.662169619365357
Que bacana!!!
Mas note que o valor da média que nos interessa nesse caso só aparece printado quando chamamos a função. O que podemos fazer para conseguir guardá-lo como uma nova variável, por exemplo?
6.5 Valor de retorno de uma função
O comando return é usado para a saída de uma função de volta ao lugar no código de onde ela foi chamada. A sintaxe desse comando é simples:
return <expression_list>Essa declaração pode ter um comando que é executado e o valor resultante devolvido. Se esse comando não for anexado ou não tiver nada além do return, a função é devolvida com o valor de None. Por exemplo:
def my_func(name,place):
print(f"Olá {name}! Você é de {place}?")
return
print(my_func("Jane","Paris"))Olá Jane! Você é de Paris?
None
Funções que não contenham o comando return sempre retornarão um valor vazio. Quando queremos utilizar uma função com o objetivo de realizar algum tipo de operação e guardar o valor dessa operação, não podemos esquecer do return. Vejamos o exemplo da função que calcula a raiz quadrada de um número:
def raiz(x):
fx = x**0.5
y = raiz(100)
print(y)None
def raiz(x):
fx = x**0.5
return fx
y = raiz(100)
print(y)10.0
6.5.1 Expectativa de vida de variáveis dentro da função
Quando você cria uma variável dentro de uma função ela é local, ou seja, só existe dentro da própria função. Por exemplo:
def concat_strings(str1,str2):
texto_concatenado = str1 + ' ' + str2
print(texto_concatenado)
texto1 = 'Que a força esteja com você,'
texto2 = 'jovem Padawan.'
concat_strings(texto1,texto2)Que a força esteja com você, jovem Padawan.
Essa função recebe dois argumentos, concatena-os e exibe o resultado em uma única linha de texto. No entanto, assim que a função é encerrada, a variável texto_concatenado é deletada. O que acontece se tentarmos acessá-la?
print(texto_concatenado)--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[23], line 1 ----> 1 print(texto_concatenado) NameError: name 'texto_concatenado' is not defined
6.5.2 Aplicação: Teorema Central do Limite - Parte 3
Retomando a função da parte 2:
def func_tcl(dist=None,intervalo=(0,1),n=100, samples=10):
if dist == None:
print('Você esqueceu de carregar uma função que defina a distribuição de X!')
else:
means = []
for j in range(0,samples):
x_func_tcl = dist(intervalo[0],intervalo[1],n)
mean_x = sum(x_func_tcl)/len(x_func_tcl)
means.append(mean_x)
mean_of_means = sum(means)/len(means)
print('Essa é a lista de médias:')
print(means)
print('\nE essa é a média das médias:')
print(mean_of_means)np.random.seed(1)
func_tcl(dist=np.random.uniform,intervalo=(-40,40),n=50,samples=10)Essa é a lista de médias:
[np.float64(-2.6419520291841945), np.float64(0.382420445207494), np.float64(-2.2971869003899186), np.float64(-0.12856511296217676), np.float64(3.945331018819081), np.float64(4.808427607983528), np.float64(1.1596981846490986), np.float64(0.4583673137720098), np.float64(-2.0177969126899638), np.float64(2.952952578448611)]
E essa é a média das médias:
0.662169619365357
Como acabamos de ver, se quisermos acessar a variável mean_of_means o Python retornará um erro. As variáveis definidas dentro de uma função tem vida curta: elas existem apenas dentro da função!
print(mean_of_means)--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[26], line 1 ----> 1 print(mean_of_means) NameError: name 'mean_of_means' is not defined
No entanto, agora já sabemos o que a palavra-chave return faz dentro de uma função. Vamos utilizá-la!
def func_tcl(dist=None,intervalo=(0,1),n=100, samples=10):
if dist == None:
print('Você esqueceu de carregar uma função que defina a distribuição de X!')
else:
means = []
for j in range(0,samples):
x_func_tcl = dist(intervalo[0],intervalo[1],n)
mean_x = sum(x_func_tcl)/len(x_func_tcl)
means.append(mean_x)
mean_of_means = sum(means)/len(means)
return mean_of_meansnp.random.seed(1)
func_tcl(dist=np.random.uniform,intervalo=(-40,40),n=50,samples=10)np.float64(0.662169619365357)
Maravilha, agora a função nos retorna apenas o que é do nosso interesse: a média das médias. Para chegar, finalmente, no mesmo resultado da aula passada basta colocar essa função dentro de um loop e tacar-lhe pau nesse carrinho, Marcos!
expoentes = [1,2,3,4,5,6,7,8,9,10]
Y = [2**exp for exp in expoentes]
means_of_means = []
np.random.seed(1)
for y in Y:
mean_of_means = func_tcl(dist=np.random.uniform,intervalo=(-40,40),n=100,samples=y)
means_of_means.append(mean_of_means)for m in means_of_means:
print(np.round(m,2))-1.17
0.92
0.37
-0.25
0.31
-0.32
0.23
-0.15
-0.06
0.03
Voilá!
Como exercício para casa, tente fazer alterações nessa função de modo que ela receba não um valor com o número de amostras, mas uma lista de números de amostras. A função deve cuspir como resultado a lista means_of_means e não apenas o valor mean_of_means. Pratique!
6.6 Documentação
Uma docstring é uma string no início de uma função, definida pelo usuário, que serve como documentação do que a função faz. A docstring vem logo depois da primeira linha que define a função e é delimitada por aspas triplas, o que permite que a string se estenda por várias linhas, como vocês devem se lembrar.
Vamos criar como exemplo uma função que printa o tipo do objeto que é passado como argumento:
def imprime_tipo(x):
'''
Função criada para a matéria EAE1106 - Métodos Computacionais para Economia
Objetivo: função simples que imprime o tipo do objeto recebido como argumento.
'''
print(type(x))Embora opcional, a documentação é uma boa prática de programação. A menos que você consiga se lembrar qual foi o cardápio do bandejão na semana passada, sempre documente seu código. Podemos acessar a documentação de determinada função utilizando o atributo __doc__.
print(imprime_tipo.__doc__)
Função criada para a matéria EAE1106 - Métodos Computacionais para Economia
Objetivo: função simples que imprime o tipo do objeto recebido como argumento.
Isso vale também para funções nativas e definidas em outros pacotes do Python. Por exemplo,
# documentação da função nativa len()
print(len.__doc__)Return the number of items in a container.
# documentação da função time() dentro do pacote time
print(time.time.__doc__)time() -> floating-point number
Return the current time in seconds since the Epoch.
Fractions of a second may be present if the system clock provides them.
# documentação da função uniform() dentro do pacote NumPy
print(np.random.uniform.__doc__)
uniform(low=0.0, high=1.0, size=None)
Draw samples from a uniform distribution.
Samples are uniformly distributed over the half-open interval
``[low, high)`` (includes low, but excludes high). In other words,
any value within the given interval is equally likely to be drawn
by `uniform`.
.. note::
New code should use the `~numpy.random.Generator.uniform`
method of a `~numpy.random.Generator` instance instead;
please see the :ref:`random-quick-start`.
Parameters
----------
low : float or array_like of floats, optional
Lower boundary of the output interval. All values generated will be
greater than or equal to low. The default value is 0.
high : float or array_like of floats
Upper boundary of the output interval. All values generated will be
less than or equal to high. The high limit may be included in the
returned array of floats due to floating-point rounding in the
equation ``low + (high-low) * random_sample()``. The default value
is 1.0.
size : int or tuple of ints, optional
Output shape. If the given shape is, e.g., ``(m, n, k)``, then
``m * n * k`` samples are drawn. If size is ``None`` (default),
a single value is returned if ``low`` and ``high`` are both scalars.
Otherwise, ``np.broadcast(low, high).size`` samples are drawn.
Returns
-------
out : ndarray or scalar
Drawn samples from the parameterized uniform distribution.
See Also
--------
randint : Discrete uniform distribution, yielding integers.
random_integers : Discrete uniform distribution over the closed
interval ``[low, high]``.
random_sample : Floats uniformly distributed over ``[0, 1)``.
random : Alias for `random_sample`.
rand : Convenience function that accepts dimensions as input, e.g.,
``rand(2,2)`` would generate a 2-by-2 array of floats,
uniformly distributed over ``[0, 1)``.
random.Generator.uniform: which should be used for new code.
Notes
-----
The probability density function of the uniform distribution is
.. math:: p(x) = \frac{1}{b - a}
anywhere within the interval ``[a, b)``, and zero elsewhere.
When ``high`` == ``low``, values of ``low`` will be returned.
If ``high`` < ``low``, the results are officially undefined
and may eventually raise an error, i.e. do not rely on this
function to behave when passed arguments satisfying that
inequality condition. The ``high`` limit may be included in the
returned array of floats due to floating-point rounding in the
equation ``low + (high-low) * random_sample()``. For example:
>>> x = np.float32(5*0.99999999)
>>> x
np.float32(5.0)
Examples
--------
Draw samples from the distribution:
>>> s = np.random.uniform(-1,0,1000)
All values are within the given interval:
>>> np.all(s >= -1)
True
>>> np.all(s < 0)
True
Display the histogram of the samples, along with the
probability density function:
>>> import matplotlib.pyplot as plt
>>> count, bins, ignored = plt.hist(s, 15, density=True)
>>> plt.plot(bins, np.ones_like(bins), linewidth=2, color='r')
>>> plt.show()
Note do exemplo acima que uma docstring pode conter uma descrição detalhada do funcionamento de uma função, inclusive com exemplos de aplicação. Legal, né? Esse tipo de documentação também está disponível para as bibliotecas como um todo. Isso pode nos ajudar, por exemplo, a conhecer o conteúdo de uma determinada biblioteca.
# documentação do NumPy
print(np.__doc__)
NumPy
=====
Provides
1. An array object of arbitrary homogeneous items
2. Fast mathematical operations over arrays
3. Linear Algebra, Fourier Transforms, Random Number Generation
How to use the documentation
----------------------------
Documentation is available in two forms: docstrings provided
with the code, and a loose standing reference guide, available from
`the NumPy homepage <https://numpy.org>`_.
We recommend exploring the docstrings using
`IPython <https://ipython.org>`_, an advanced Python shell with
TAB-completion and introspection capabilities. See below for further
instructions.
The docstring examples assume that `numpy` has been imported as ``np``::
>>> import numpy as np
Code snippets are indicated by three greater-than signs::
>>> x = 42
>>> x = x + 1
Use the built-in ``help`` function to view a function's docstring::
>>> help(np.sort)
... # doctest: +SKIP
For some objects, ``np.info(obj)`` may provide additional help. This is
particularly true if you see the line "Help on ufunc object:" at the top
of the help() page. Ufuncs are implemented in C, not Python, for speed.
The native Python help() does not know how to view their help, but our
np.info() function does.
Available subpackages
---------------------
lib
Basic functions used by several sub-packages.
random
Core Random Tools
linalg
Core Linear Algebra Tools
fft
Core FFT routines
polynomial
Polynomial tools
testing
NumPy testing tools
distutils
Enhancements to distutils with support for
Fortran compilers support and more (for Python <= 3.11)
Utilities
---------
test
Run numpy unittests
show_config
Show numpy build configuration
__version__
NumPy version string
Viewing documentation using IPython
-----------------------------------
Start IPython and import `numpy` usually under the alias ``np``: `import
numpy as np`. Then, directly past or use the ``%cpaste`` magic to paste
examples into the shell. To see which functions are available in `numpy`,
type ``np.<TAB>`` (where ``<TAB>`` refers to the TAB key), or use
``np.*cos*?<ENTER>`` (where ``<ENTER>`` refers to the ENTER key) to narrow
down the list. To view the docstring for a function, use
``np.cos?<ENTER>`` (to view the docstring) and ``np.cos??<ENTER>`` (to view
the source code).
Copies vs. in-place operation
-----------------------------
Most of the functions in `numpy` return a copy of the array argument
(e.g., `np.sort`). In-place versions of these functions are often
available as array methods, i.e. ``x = np.array([1,2,3]); x.sort()``.
Exceptions to this rule are documented.
Podemos usar sempre esse atalho caso desejemos conhecer as funcionalidades que uma biblioteca guarda por trás de suas cortinas!
6.7 Funções anônimas
Em Python, uma função anônima é uma função definida sem nome. Quem poderia imaginar, não é mesmo?
Enquanto as funções normais são definidas usando a palavra-chave def em Python, as funções anônimas são definidas usando a palavra-chave lambda. Portanto, funções anônimas também são chamadas de funções lambda. A estrutura usual de uma função lambda é a seguinte:
lambda <argumentos>: <expressão>Usualmente a gente precisa de uma função lambda porque precisamos de uma função rápida por um período de tempo e/ou quando a gente usa técnicas mais poderosas que possuem funções como argumento, como filter e map. Vou fazer um exemplo com cada uma delas.
Usando filter
A função filter() em Python recebe uma função e uma lista como argumentos. A função é chamada com todos os itens da lista e uma nova lista é retornada contendo itens para os quais a função avalia True. Aqui está um exemplo de uso da função para filtrar apenas números pares de uma lista.
my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(filter(lambda x: (x%2 == 0) , my_list))
print(new_list)[4, 6, 8, 12]
Usando map
A função map() em Python recebe uma função e uma lista. A função é chamada com todos os itens da lista e uma nova lista é retornada contendo os itens retornados por essa função para cada item. Aqui está um exemplo de uso da função map() para dobrar todos os itens em uma lista.
my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(map(lambda x: x * 2 , my_list))
print(new_list)[2, 10, 8, 12, 16, 22, 6, 24]
6.8 Conclusão
Funções representam o principal mecanismo de organização da lógica de um programa. Elas permitem decompor problemas complexos em partes menores, reutilizar código, reduzir redundância e tornar explícita a relação entre entradas e saídas. Do ponto de vista computacional, introduzem o conceito de escopo, estruturam o fluxo de execução e tornam o código mais legível e testável.
No contexto da Economia aplicada, funções são particularmente relevantes porque grande parte do trabalho empírico envolve transformar dados em resultados por meio de rotinas repetidas: cálculo de estatísticas, limpeza de bases, simulações ou estimações. Escrever essas rotinas como funções facilita replicação, comparação de cenários e organização de projetos.
Nos próximos capítulos, ao utilizar bibliotecas como NumPy e Pandas, veremos que praticamente todas as operações são estruturadas como chamadas de funções ou métodos. A compreensão sólida do que é uma função é condição necessária para avançar com segurança em programação numérica e análise de dados.
6.9 Exercícios
X
X