Skip to content

Commit c1969db

Browse files
authored
Merge pull request #295 from z4r4tu5tr4/pelican
Programação funcional parte 2; funções
2 parents 8c4cbfb + e296192 commit c1969db

File tree

2 files changed

+273
-6
lines changed

2 files changed

+273
-6
lines changed

content/programacao-funcional-com-python-0.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
1-
title: Programação funcional com Python #0- saindo da zona de conforto
1+
title: Programação funcional com Python #0 - Saindo da zona de conforto
22
Slug: progrmacao-funcional-com-python-0
33
Date: 2017-11-20 19:43
44
Tags: python,fp,funcional,functional programming
55
Author: Eduardo Mendes
66
77
Github: z4r4tu5tr4
8-
Site: youtube.com/c/eduardomendes
8+
Site: http://youtube.com/c/eduardomendes
99
Linkedin: mendesxeduardo
1010
Category: programação funcional
1111

12-
<figure style="float:left;">
13-
<img src="/images/z4r4tu5tr4/lambda.jpg">
14-
</figure>
15-
</br>
1612

1713
# 0. Saindo da zona de conforto
1814

@@ -150,3 +146,5 @@ Outro grande homem e que vale a pena mencionar e ser buscado é o [Haskell Curry
150146
A primeira linguagem funcional 'oficial' (não gosto muito de dizer isso) é o Lisp (List Processing) criada pelo fenomenal [John McCarthy](https://pt.wikipedia.org/wiki/John_McCarthy) que também vale a pena ser pesquisado e estudado.
151147

152148
Veremos o básico sobre os tipos de função no próximo tópico.
149+
150+
OBS: [Referências](https://github.com/z4r4tu5tr4/python-funcional/blob/master/referencias.md) usadas durante todos os tópicos.
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
title: Programação funcional com Python #1 - Funções
2+
Slug: progrmacao-funcional-com-python-1
3+
Date: 2017-12-08 13:30
4+
Tags: python,fp,funcional,functional programming
5+
Author: Eduardo Mendes
6+
7+
Github: z4r4tu5tr4
8+
Site: http://youtube.com/c/eduardomendes
9+
Linkedin: mendesxeduardo
10+
Category: programação funcional
11+
12+
# 1. Funções
13+
14+
Como nem tudo são flores, vamos começar do começo e entender algumas características das funções do python (o objeto função) e dar uma revisada básica em alguns conceitos de função só pra gente não se perder no básico depois. Então o primeiro tópico vai se limitar a falar da estrutura básica das funções em python, sem entrar profundamente em cada um dos tópicos. Será uma explanação de código e abrir a cabeça para novas oportunidades de código mais pythonicos e que preferencialmente gere menos efeito colateral. Mas calma, não vamos ensinar a fazer funções, você já está cheio disso.
15+
16+
## 1.1 Funções como objeto de primeira classe
17+
18+
Funções como objeto de primeira classe, são funções que se comportam como qualquer tipo nativo de uma determinada linguagem. Por exemplo:
19+
20+
```python
21+
# uma lista
22+
23+
lista = [1, 'str', [1,2], (1,2), {1,2}, {1: 'um'}]
24+
```
25+
26+
Todos esses exemplos são tipos de objetos de primeira classe em Python, mas no caso as funções também são. Como assim? Pode-se passar funções como parâmetro de uma outra função, podemos armazenar funções em variáveis, pode-se definir funções em estruturas de dados:
27+
28+
```python
29+
# Funções como objeto de primeira classe
30+
31+
func = lambda x: x # a função anônima, lambda, foi armazenada em uma variável
32+
33+
def func_2(x):
34+
return x + 2
35+
36+
lista = [func, func_2] # a variável que armazena a função foi inserida em uma estrutura, assim como uma função gerada com def
37+
38+
lista_2 = [lambda x: x, lambda x: x+1] # aqui as funções foram definidas dentro de uma estrutura
39+
```
40+
41+
Como é possível notar, em python, as funções podem ser inseridas em qualquer contexto e também geradas em tempo de execução. Com isso nós podemos, além de inserir funções em estruturas, retornar funções, passar funções como parâmetro (HOFs), definir funções dentro de funções(closures) e assim por diante. Caso você tenha aprendido a programar usando uma linguagem em que as funções não são objetos de primeira classe, não se assuste. Isso faz parte da rotina comum do python. Preferencialmente, e quase obrigatoriamente, é melhor fazer funções simples, pequenas e de pouca complexidade para que elas não sofram interferência do meio externo, gerem menos manutenção e o melhor de tudo, possam ser combinadas em outras funções. Então vamos lá!
42+
43+
## 1.2 Funções puras
44+
45+
Funções puras são funções que não sofrem interferência do meio externo. Vamos começar pelo exemplo ruim:
46+
47+
```python
48+
49+
valor = 5
50+
51+
def mais_cinco(x):
52+
return x + valor
53+
54+
assert mais_cinco(5) == 10 # True
55+
56+
valor = 7
57+
58+
assert mais_cinco(5) == 10 # AssertionError
59+
```
60+
61+
`mais_cinco()` é o exemplo claro de uma função que gera efeito colateral. Uma função pura deve funcionar como uma caixa preta, todas as vezes em que o mesmo input for dado nela, ela terá que retornar o mesmo valor. Agora vamos usar o mesmo exemplo, só alterando a linha do return:
62+
63+
```python
64+
65+
valor = 5
66+
67+
def mais_cinco(x):
68+
return x + 5
69+
70+
assert mais_cinco(5) == 10 # True
71+
72+
valor = 7
73+
74+
assert mais_cinco(5) == 10 # True
75+
```
76+
77+
Pode parecer trivial, mas muitas vezes por comodidade deixamos o meio influenciar no comportamento de uma função. Por definição o Python só faz possível, e vamos falar disso em outro tópico, a leitura de variáveis externas. Ou seja, dentro do contexto da função as variáveis externas não podem ser modificadas, mas isso não impede que o contexto externo a modifique. Se você for uma pessoa inteligente como o Jaber deve saber que nunca é uma boa ideia usar valores externos. Mas, caso seja necessário, você pode sobrescrever o valor de uma variável no contexto global usando a palavra reservada `global`. O que deve ficar com uma cara assim:
78+
79+
```Python
80+
81+
valor = 5
82+
83+
def teste():
84+
global valor # aqui é feita a definição
85+
valor = 7
86+
87+
print(valor) # 7
88+
```
89+
90+
Só lembre-se de ser sempre coerente quando fizer isso, as consequências podem ser imprevisíveis. Nessa linha de funções puras e pequeninas, podemos caracterizar, embora isso não as defina, funções de ordem superior, que são funções que recebem uma função como argumento, ou as devolvem, e fazem a chamada das mesmas dentro do contexto da função que a recebeu como parâmetro. Isso resulta em uma composição de funções, o que agrega muito mais valor caso as funções não gerem efeitos colaterais.
91+
92+
93+
## 1.3 Funções de ordem superior (HOFs)
94+
95+
Funções de ordem superior são funções que recebem funções como argumento(s) e/ou retornam funções como resposta. Existem muitas funções embutidas em python de ordem superior, como: `map, filter, zip` e praticamente todo o módulo functools `import functools`. Porém, nada impede de criarmos novas funções de ordem superior. Um ponto a ser lembrado é que map e filter não tem mais a devida importância em python com a entrada das comprehensions (embora eu as adore), o que nos faz escolher única e exclusivamente por gosto, apesar de comprehensions serem mais legíveis (vamos falar disso em outro contexto), existem muitos casos onde elas ainda fazem sentido. Mas sem me estender muito, vamos ao código:
96+
97+
```python
98+
99+
func = lambda x: x+2 # uma função simples, soma mais 2 a qualquer inteiro
100+
101+
def func_mais_2(funcao, valor):
102+
"""
103+
Executa a função passada por parâmetro e retorna esse valor somado com dois
104+
105+
Ou seja, é uma composição de funções:
106+
107+
Dado que func(valor) é processado por func_func:
108+
func_mais_2(func(valor)) == f(g(x))
109+
"""
110+
return funcao(valor) + 2
111+
```
112+
113+
Um ponto a tocar, e o que eu acho mais bonito, é que a função vai retornar diferentes respostas para o mesmo valor, variando a entrada da função. Nesse caso, dada a entrada de um inteiro ele será somado com 2 e depois com mais dois. Mas, vamos estender este exemplo:
114+
115+
```python
116+
117+
func = lambda x: x + 2 # uma função simples, soma mais 2 a qualquer inteiro
118+
119+
def func_mais_2(funcao, valor):
120+
"""
121+
Função que usamos antes.
122+
"""
123+
return funcao(valor) + 2
124+
125+
assert func_mais_2(func, 2) == 6 # true
126+
127+
def func_quadrada(val):
128+
"""
129+
Eleva o valor de entrada ao quadrado.
130+
"""
131+
return val * val
132+
133+
assert func_mais_2(func_quadrada, 2) == 6 # true
134+
```
135+
136+
### 1.3.1 Um exemplo usando funções embutidas:
137+
138+
Muitas das funções embutidas em python são funções de ordem superior (HOFs) como a função map, que é uma das minhas preferidas. Uma função de map recebe uma função, que recebe um único argumento e devolve para nós uma nova lista com a função aplicada a cada elemento da lista:
139+
140+
```python
141+
def func(arg):
142+
return arg + 2
143+
144+
lista = [2, 1, 0]
145+
146+
list(map(func, lista)) == [4, 3, 2] # true
147+
148+
```
149+
150+
Mas fique tranquilo, falaremos muito mais sobre isso.
151+
152+
## 1.4 `__call__`
153+
154+
Por que falar de classes? Lembre-se, Python é uma linguagem construída em classes, e todos os objetos que podem ser chamados/invocados implementam o método `__call__`:
155+
156+
Em uma função anônima:
157+
158+
```python
159+
func = lambda x: x
160+
161+
'__call__' in dir(func) # True
162+
```
163+
164+
Em funções tradicionais:
165+
```python
166+
def func(x):
167+
return x
168+
169+
'__call__' in dir(func) # True
170+
```
171+
172+
Isso quer dizer que podemos gerar classes que se comportam como funções?
173+
174+
SIIIIIM. Chupa Haskell
175+
176+
Essa é uma parte interessante da estrutura de criação do Python a qual veremos mais em outro momento sobre introspecção de funções, mas vale a pena dizer que classes, funções nomeadas, funções anônimas e funções geradoras usam uma base comum para funcionarem, essa é uma das coisas mais bonitas em python e que em certo ponto fere a ortogonalidade da linguagem, pois coisas iguais tem funcionamentos diferentes, mas facilita o aprendizado da linguagem, mas não é nosso foco agora.
177+
178+
## 1.5 Funções geradoras
179+
180+
Embora faremos um tópico extremamente focado em funções geradoras, não custa nada dar uma palinha, não?
181+
182+
Funções geradoras são funções que nos retornam um iterável. Mas ele é lazy (só é computado quando invocado). Para exemplo de uso, muitos conceitos precisam ser esclarecidos antes de entendermos profundamente o que acontece com elas, mas digo logo: são funções lindas <3
183+
184+
Para que uma função seja geradora, em tese, só precisamos trocar o return por yield:
185+
186+
```python
187+
188+
def gen(lista):
189+
for elemento in lista:
190+
yield elemento
191+
192+
gerador = gen([1, 2, 3, 4, 5])
193+
194+
next(gerador) # 1
195+
next(gerador) # 2
196+
next(gerador) # 3
197+
next(gerador) # 4
198+
next(gerador) # 5
199+
next(gerador) # StopIteration
200+
```
201+
202+
Passando bem por cima, uma função geradora nos retorna um iterável que é preguiçoso. Ou seja, ele só vai efetuar a computação quando for chamado.
203+
204+
205+
## 1.6 Funções anônimas (lambda)
206+
207+
Funções anônimas, ou funções lambda, são funções que podem ser declaradas em qualquer contexto. Tá... Todo tipo de função em python pode ser declarada em tempo de execução. Porém funções anônimas podem ser atribuídas a variáveis, podem ser definidas dentro de sequências e declaradas em um argumento de função. Vamos olhar sua sintaxe:
208+
209+
```python
210+
lambda argumento: argumento
211+
```
212+
213+
A palavra reservada `lambda` define a função, assim como uma `def`. Porém em uma `def` quase que instintivamente sempre quebramos linha:
214+
215+
```Python
216+
def func():
217+
pass
218+
```
219+
220+
Uma das diferenças triviais em python é que as funções anônimas não tem nome. Tá, isso era meio óbvio, mas vamos averiguar:
221+
222+
```Python
223+
def func():
224+
pass
225+
226+
func.__name__ # func
227+
228+
lambda_func = lambda arg: arg
229+
230+
lambda_func.__name__ # '<lambda>'
231+
```
232+
233+
O resultado `'<lambda>'` será o mesmo para qualquer função. Isso torna sua depuração praticamente impossível em python. Por isso os usuários de python (e nisso incluo todos os usuários, até aqueles que gostam de funcional) não encorajam o uso de funções lambda a todos os contextos da linguagem. Mas, em funções que aceitam outra funções isso é meio que uma tradição, caso a função (no caso a que executa o código a ser usado pelo lambda) não esteja definida e nem seja reaproveitada em outro contexto. Eu gosto de dizer que lambdas são muito funcionais em aplicações parciais de função. Porém, os lambdas não passam de açúcar sintático em Python, pois não há nada que uma função padrão (definida com `def`), não possa fazer de diferente. Até a introspecção retorna o mesmo resultado:
234+
235+
236+
```Python
237+
def func():
238+
pass
239+
240+
type(func) # function
241+
242+
lambda_func = lambda arg: arg
243+
244+
type(lambda_func) # function
245+
```
246+
247+
Uma coisa que vale ser lembrada é que funções anônimas em python só executam uma expressão. Ou seja, não podemos usar laços de repetição (`while`, `for`), tratamento de exceções (`try`, `except`, `finally`). Um simples `if` com uso de `elif` também não pode ser definido. Como sintaticamente só são aceitas expressões, o único uso de um `if` é o ternário:
248+
249+
250+
```Python
251+
valor_1 if condicao else valor_2
252+
```
253+
254+
O que dentro de um lambda teria essa aparência:
255+
256+
257+
```Python
258+
func = lambda argumento: argumento + 2 if argumento > 0 else argumento - 2
259+
```
260+
261+
Funções lambda também podem ter múltiplos argumentos, embora seu processamento só possa ocorrer em uma expressão:
262+
263+
```Python
264+
func = lambda arg_1, arg_2, arg_3: True if sum([arg_1, arg_2, arg_3]) > 7 else min([arg_1, arg_2, arg_3])
265+
```
266+
267+
Embora essa seja uma explanação inicial sobre as funções anônimas, grande parte dos tópicos fazem uso delas e vamos poder explorar melhor sua infinitude.
268+
269+
Mas por hoje é só e no tópico seguinte vamos discutir, mesmo que superficialmente, iteradores e iteráveis e suas relações com a programação funcional.

0 commit comments

Comments
 (0)