associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

Diferenças para "PythonFuncional"

Diferenças entre as versões de 1 e 15 (14 versões de distância)
Revisão 1e 2003-12-23 20:51:20
Tamanho: 8415
Editor: 3-245
Comentário:
Revisão 15e 2011-02-27 20:09:52
Tamanho: 10119
Editor: RudaMoura
Comentário:
Deleções são marcadas assim. Adições são marcadas assim.
Linha 1: Linha 1:
Programação Funcional em Python
Rudá Moura <ruda@haxent.com.br>
Dezembro de 2003

Resumo
'''Programação Funcional em Python - Rudá Moura <ruda.moura@gmail.com><<BR>>
Dezembro de 2003. Revisado em 2011.'''

Resumo:
Linha 18: Linha 17:

1. Primeiras Palavras
<<TableOfContents>>

=
Primeiras Palavras =
Linha 23: Linha 23:
A lista é um tipo de dado dinâmico (pode aumentar ou diminuir de tamanho) e A ''lista'' é um tipo de dado dinâmico (pode aumentar ou diminuir de tamanho) e
Linha 27: Linha 27:
O Python provê um conjunto de funções que podem ser aplicados a listas.
Pode-se inserir ou remover elementos, classificar, tratar a lista como se
O Python provê um conjunto de funções que podem ser aplicados as listas.
Pode-se inserir ou remover elementos, classificar/ordenar, tratar a lista como se
Linha 32: Linha 32:
{{{
#!python
Linha 36: Linha 38:

Uma tupla é semelhante a uma lista mas esta é estática (não possui métodos para
inserir ou remover elementos) e imutável (os elementos individuais não podem
ser alterados). Uma string, que também é um tipo estático e imutável, pode ter
um tratamento análogo ao de uma lista. Define-se então o termo seqüencia para
indicar uma tupla, uma string ou uma lista.
}}}

Uma ''tupla'' é semelhante a uma lista mas esta é estática (não possui métodos para inserir ou remover elementos) e imutável (os elementos individuais não podem ser alterados). Um exemplo de tupla pode ser um par de elementos, como em (-1, 1) ou um conjunto de pares, como em ( (0, 0), (1, 1), (-1, -1) ).

Uma ''
string'', que também é um tipo estático e imutável e pode ter um tratamento análogo ao de uma lista.

Define-se então o termo genérico ''seqüência'' para indicar uma tupla, uma string ou uma lista.
Note que não existe um tipo seqüência em Python.

{{{
#!python
Linha 47: Linha 53:

2. A Expressão Lambda

Uma expressão lambda permite escrever funções anônimas ou sem-nome usando
apenas uma linha de código.

Sintaxe:
}}}

=
A Expressão Lambda =

Uma ''expressão lambda'' permite escrever funções anônimas/sem-nome usando apenas uma linha de código.

Sintaxe:

{{{
Linha 56: Linha 63:

Onde var1, var2, ..., varN são variáveis que representam o argumento da função
e expr é qualquer expressão válida em Python envolvendo essas variáveis.
O resultado é uma nova expressão expr_ret.

Um exemplo prático de lambda é uma função que recebe dois valores (x e y) e
retorna a soma desses elementos.

Exemplos:
}}}

Onde ''var1, var2, ..., varN'' são variáveis que representam o argumento da função
e ''expr'' é qualquer expressão válida em Python envolvendo essas variáveis.
O resultado é uma nova expressão ''expr_ret''.

Um exemplo trivial de lamba é a função identidade, que retorna o próprio elemento.

Um exemplo prático de lambda é uma função que recebe dois valores (x e y) e retorna a soma desses elementos.

Exemplos:

Função identidade.
{{{
#!python
>>> lambda x: x
<function <lambda> at 0x1e3630>
}}}

Soma de dois valores.
{{{
#!python
Linha 67: Linha 85:
<function <lambda> at 0x81a8d84>

Como posso obter algo útil, por exemplo 1+2?
<function <lambda> at 0x1e3670>
}}}

Note que o resultado é uma expressão do tipo lambda. Mas c
omo posso obter algo útil, por exemplo, calcular 1+2 com lambda?

{{{
#!python
Linha 73: Linha 94:
}}}
Linha 75: Linha 97:
{{{
#!python
>>> identidade = lambda x: x
>>> identidade('Python')
'Python'
Linha 77: Linha 103:
>>> soma
<function <lambda> at 0x81a8e2c>
Linha 81: Linha 105:
}}}
Linha 83: Linha 108:
que se seguem faremos melhor uso dela.


3
. As Funções Embutidas Map, Reduce, Filter e Zip.

3.1.
Map

A forma mais simples de se usar o map é aplicando uma função func a uma
seqüência seq, o resultado é sempre uma lista cujo os elementos são obtidos
que se seguem faremos melhor uso dela. O importante é ter em mente que uma expressão lambda pode responder igual a qualquer função em Python.

=
As Funções Embutidas Map, Reduce, Filter e Zip =

==
Map ==

A forma mais simples de se usar o map é aplicando uma função ''func'' a uma
seqüência ''seq'', o resultado é sempre uma lista cujo os elementos são obtidos
Linha 93: Linha 117:
uma lista com None retorna-se os elementos originais da seqüência.

Sintaxe:
uma lista com ''None'' retorna-se os elementos originais da seqüência.

Sintaxe:
{{{
#!python
Linha 98: Linha 123:

Exemplos:
}}}

Exemplos:
{{{
#!python
Linha 103: Linha 130:
Linha 106: Linha 132:
Linha 109: Linha 134:
Linha 112: Linha 136:
}}}
Linha 115: Linha 140:
{{{
#!python
Linha 117: Linha 144:
}}}
Linha 120: Linha 148:
{{{
#!python
Linha 124: Linha 154:

Onde M é o tamanho de seq.

3.1.1. Map Completo

A forma completa de map admite uma função func (ou None) e um conjunto de
seqüencias (seq1, seq2, ..., seqN) como parâmetros.

Sintaxe:
}}}

Onde ''M'' é o tamanho de seq.

=== Forma Completa de Map ===
A forma completa de map admite uma função ''func'' (ou ''None'') e um conjunto de
seqüencias (''seq1, seq2, ..., seqN'') como parâmetros.

Sintaxe:
{{{
#!python
Linha 135: Linha 166:

Atribuindo-se None ao map este retorna sempre uma tupla de N elementos que
}}}

Atribuindo-se ''None'' ao map este retorna sempre uma tupla de N elementos que
Linha 141: Linha 173:
{{{
#!python
Linha 144: Linha 177:
Linha 147: Linha 179:
Linha 150: Linha 181:
}}}
Linha 153: Linha 185:
{{{
#!python
Linha 157: Linha 191:

Onde M é o tamanho da maior lista entre seq1, seq2, ..., seqN.
}}}

Onde ''M'' é o tamanho da maior lista entre seq1, seq2, ..., seqN.
Linha 168: Linha 203:
{{{
#!python
Linha 170: Linha 207:
Linha 173: Linha 209:
Linha 178: Linha 213:

Oops
.

3.2. Reduce

A função reduce aplica acumuladamente os ítens de uma seqüência de entrada (da
esquerda para a direita) a uma função de dois parâmetros até reduzir esse
}}}

A lista [2, 4, 6] tem um elemento a menos do que [2, 2, 2, 2] e é preenchida com None, por isso o erro
.

== Reduce ==

A função ''reduce'' aplica acumuladamente os ítens de uma seqüência de entrada ''seq'' (da
esquerda para a direita) a uma função ''func'' de dois parâmetros até reduzir esse
Linha 186: Linha 222:
valor inicial como parâmetro.

Sintaxe:
valor ''inicial'' como parâmetro.

Sintaxe:
{{{
#!python
Linha 191: Linha 228:

Exemplos:
}}}

Exemplos:

{{{
#!python
Linha 196: Linha 236:
Linha 199: Linha 238:

3.3.
Filter

A função filter retorna uma seqüência cujos os valores são os elementos da
seqüência de entrada que respeitam o seguinte critério: se a função for None,
os elementos que são verdadeiros são adicionados,
caso uma função esteja
presente o valor de retorno da função é utilizado como valor verdade e apenas
os elementos verdadeir
os vão fazer parte da seqüência de retorno.

Sintaxe:
}}}

==
Filter ==

A função ''filter'' retorna uma seqüência ''seq_ret'' cujos valores são os elementos da
seqüência de entrada ''seq'' que respeitam o seguinte critério: se a função for ''None''
os elementos que são verdadeiros são adicionados
, caso uma função ''func'' esteja
definida o valor de retorno da função é utilizado como valor verdade e apenas
esses element
os vão fazer parte da seqüência de retorno.

Sintaxe:
{{{
#!python
Linha 211: Linha 252:

A seqüencia seq_ret tem sempre o mesmo tipo de seq.

Exemplos:
}}}

A seqüência ''seq_ret'' tem sempre o mesmo tipo de ''seq''.

Exemplos:
{{{
#!python
Linha 218: Linha 261:
Linha 221: Linha 263:

3.4.
Zip

A função zip retorna uma seqüência cujos os elementos são tuplas resultantes
de cada um dos elementos, de uma ou mais seqüencias de entrada. A seqüência
>>> filter(lambda s: s > 'a', 'python r0cks!')
'pythonrcks'
}}}

==
Zip ==

A função ''zip'' retorna uma seqüência ''seq_ret'' cujos elementos são tuplas resultantes
de cada um dos elementos de uma ou mais seqüências de entrada ''seq1, seq2, ..., seqN''. A seqüência
Linha 229: Linha 274:
{{{
#!python
Linha 231: Linha 277:

Onde seq_ret é obtida através do seguinte logicismo:
}}}

Onde ''seq_ret'' é obtida através do seguinte logicismo:

{{{
#!python
Linha 238: Linha 287:

Onde m é o comprimento da menor lista em seq.

Exemplos:
}}}

O valor de ''m'' é o comprimento da menor lista em ''seq''.

Exemplos:

{{{
#!python
Linha 245: Linha 297:
Linha 248: Linha 299:


4. Conclusão
}}}

= Exemplos Mais Complexos =
Linha 254: Linha 305:
{{{
#!python
Linha 275: Linha 328:
}}}

Programação Funcional em Python - Rudá Moura <ruda.moura@gmail.com>
Dezembro de 2003. Revisado em 2011.

Resumo:

O objetivo desse artigo é explicar de maneira rápida e de fácil entendimento as características funcionais da Linguagem Python (lambda, map, filter e zip). Entende-se aqui como funcional o paradigma de programação utilizado em linguagens como Lisp e Scheme. O leitor não deve se assustar pois não será necessário contar parênteses para tirar proveito dessas características em Python, (((isto foi uma piada))).

O aspecto funcional de Python permite escrever código compacto e eventualmente mais rápido, por outro lado corre-se o risco de deixar o código obscuro e de manutenção mais complicada. Tenha bom senso e cuidado com o seu uso.

Primeiras Palavras

Uma das vantagens da linguagem Python está no seu rico conjunto de estruturas de dados disponíveis ao programador. Entre essas estruturas destaca-se a lista. A lista é um tipo de dado dinâmico (pode aumentar ou diminuir de tamanho) e mutável (seus elementos individuais podem ser alterados) utilizado para armazenar outros tipos de dados, inclusive outras listas.

O Python provê um conjunto de funções que podem ser aplicados as listas. Pode-se inserir ou remover elementos, classificar/ordenar, tratar a lista como se fosse uma pilha entre outros. É comum também "caminhar" entre os seus elementos, como no exemplo a seguir.

   1 lista = [0, 1, 2, 3, 4, 5]
   2 soma = 0
   3 for elem in lista:
   4     soma = soma + elem

Uma tupla é semelhante a uma lista mas esta é estática (não possui métodos para inserir ou remover elementos) e imutável (os elementos individuais não podem ser alterados). Um exemplo de tupla pode ser um par de elementos, como em (-1, 1) ou um conjunto de pares, como em ( (0, 0), (1, 1), (-1, -1) ).

Uma string, que também é um tipo estático e imutável e pode ter um tratamento análogo ao de uma lista.

Define-se então o termo genérico seqüência para indicar uma tupla, uma string ou uma lista. Note que não existe um tipo seqüência em Python.

   1 # Seqüências
   2 tupla = (0, 1, 2)
   3 lista = [0, 1, 2]
   4 string = 'doesnotcompute'

A Expressão Lambda

Uma expressão lambda permite escrever funções anônimas/sem-nome usando apenas uma linha de código.

Sintaxe:

lambda [ var1, var2, ..., varN ]: expr -> expr_ret

Onde var1, var2, ..., varN são variáveis que representam o argumento da função e expr é qualquer expressão válida em Python envolvendo essas variáveis. O resultado é uma nova expressão expr_ret.

Um exemplo trivial de lamba é a função identidade, que retorna o próprio elemento. Um exemplo prático de lambda é uma função que recebe dois valores (x e y) e retorna a soma desses elementos.

Exemplos:

Função identidade.

   1 >>> lambda x: x
   2 <function <lambda> at 0x1e3630>

Soma de dois valores.

   1 >>> lambda x, y: x+y
   2 <function <lambda> at 0x1e3670>

Note que o resultado é uma expressão do tipo lambda. Mas como posso obter algo útil, por exemplo, calcular 1+2 com lambda?

   1 >>> (lambda x, y: x+y)(1,2)
   2 3

Uma maneira de "lembrar" uma expressão lambda é guardá-la em uma variável.

   1 >>> identidade = lambda x: x
   2 >>> identidade('Python')
   3 'Python'
   4 >>> soma = lambda x, y: x+y
   5 >>> soma(1, 2)
   6 3

Aparentemente lambda sozinho não tem muita graça ou utilidade, mas nas seções que se seguem faremos melhor uso dela. O importante é ter em mente que uma expressão lambda pode responder igual a qualquer função em Python.

As Funções Embutidas Map, Reduce, Filter e Zip

Map

A forma mais simples de se usar o map é aplicando uma função func a uma seqüência seq, o resultado é sempre uma lista cujo os elementos são obtidos aplicando-se individualmente cada elemento de seq a função func. Ao mapear-se uma lista com None retorna-se os elementos originais da seqüência.

Sintaxe:

   1 map(func ou None, seq) -> lista

Exemplos:

   1 >>> map(None, [1, 3, 5])
   2 [1, 3, 5]
   3 >>> map(abs, [-1, -2, -3])
   4 [1, 2, 3]
   5 >>> map(str, [2, 4, 6])
   6 ['2', '4', '6']
   7 >>> map(hex, (10, 11, 12))
   8 ['0xa', '0xb', '0xc']

Um exemplo mais interessante com uso da expressão lambda.

   1 >>> map(lambda x: x*x*x, [1, 2, 3, 4])
   2 [1, 8, 27, 64]

Em resumo, se oferecermos uma função ao map, teremos que:

   1 lista[0] = func(seq[0])
   2 lista[1] = func(seq[1])
   3 ...
   4 lista[M-1] = func(seq[M-1])

Onde M é o tamanho de seq.

Forma Completa de Map

A forma completa de map admite uma função func (ou None) e um conjunto de seqüencias (seq1, seq2, ..., seqN) como parâmetros.

Sintaxe:

   1 map(func ou None, seq1, seq2, ..., seqN) -> lista

Atribuindo-se None ao map este retorna sempre uma tupla de N elementos que são os elementos cuja ordem deles é a ordem das seqüencias até o tamanho da maior lista, índices maiores que as seqüências são retornados como None.

Exemplos:

   1 >>> map(None, [1, 3, 5], [2, 4, 6])
   2 [(1, 2), (3, 4), (5, 6)]
   3 >>> map(None, [1, 3, 5], [2, 4, 6, 8, 10])
   4 [(1, 2), (3, 4), (5, 6), (None, 8), (None, 10)]
   5 >>> map(None, (1, 3, 5), (2, 4, 6), (2, 4, 8, 16, 32))
   6 [(1, 2, 2), (3, 4, 4), (5, 6, 8), (None, None, 16), (None, None, 32)]

Se atribuirmos uma função para map, teremos sempre que:

   1 lista[0] = func(seq1[0], seq2[0], ..., seqN[0])
   2 lista[1] = func(seq1[1], seq2[1], ..., seqN[1])
   3 ...
   4 lista[M-1] = func(seq1[M-1], seq2[M-1], ..., seqN[M-1])

Onde M é o tamanho da maior lista entre seq1, seq2, ..., seqN.

Fica fácil entender que se oferecermos ao map três listas é necessário então que a função aceite três variáveis como parâmetro. As seqüencias menores que M são automaticamente preenchidas com o valor None e isso pode ser um inconveniente caso se tente aplicar a uma função que espera valores numéricos, pois None nunca é tratado como um valor numérico.

Exemplos:

   1 >>> map(lambda x, y: x*y, [1, 3, 5], [2, 4, 6])
   2 [2, 12, 30]
   3 >>> map(lambda a, b, x: a*x+b, [1, 3, 5], [2, 4, 8], [0, 0, 0])
   4 [2, 4, 8]
   5 >>> map(pow, [2, 4, 6], [2, 2, 2, 2])
   6 Traceback (most recent call last):
   7   File "<stdin>", line 1, in ?
   8 TypeError: unsupported operand type(s) for ** or pow(): 'NoneType' and 'int'

A lista [2, 4, 6] tem um elemento a menos do que [2, 2, 2, 2] e é preenchida com None, por isso o erro.

Reduce

A função reduce aplica acumuladamente os ítens de uma seqüência de entrada seq (da esquerda para a direita) a uma função func de dois parâmetros até reduzir esse cálculo a um único valor de resposta. Opcionalmente pode-se atribuir um valor inicial como parâmetro.

Sintaxe:

   1 reduce(func, seq[, inicial]) -> valor

Exemplos:

   1 >>> reduce(lambda x, y: x+y, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
   2 55
   3 >>> reduce(lambda x, y: x+y, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 100)
   4 155

Filter

A função filter retorna uma seqüência seq_ret cujos valores são os elementos da seqüência de entrada seq que respeitam o seguinte critério: se a função for None os elementos que são verdadeiros são adicionados, caso uma função func esteja definida o valor de retorno da função é utilizado como valor verdade e apenas esses elementos vão fazer parte da seqüência de retorno.

Sintaxe:

   1 filter(func ou None, seq) -> seq_ret

A seqüência seq_ret tem sempre o mesmo tipo de seq.

Exemplos:

   1 >>> filter(None, [0, 1, 2, None, 2>1, '', 'ola'])
   2 [1, 2, True, 'ola']
   3 >>> filter(lambda x: x > 3, [0, 1, 2, 3, 4, 5])
   4 [4, 5]
   5 >>> filter(lambda s: s > 'a', 'python r0cks!')
   6 'pythonrcks'

Zip

A função zip retorna uma seqüência seq_ret cujos elementos são tuplas resultantes de cada um dos elementos de uma ou mais seqüências de entrada seq1, seq2, ..., seqN. A seqüência resultante é sempre truncada ao tamanho da menor seqüência apresentada.

Sintaxe:

   1 zip(seq1, seq2, ..., seqN) -> seq_ret

Onde seq_ret é obtida através do seguinte logicismo:

   1 seq_ret[0] = (seq1[0], seq2[0], ..., seqN[0])
   2 seq_ret[1] = (seq1[1], seq2[1], ..., seqN[1])
   3 ...
   4 seq_ret[m-1] = (seq1[m-1], seq2[m-1], ..., seqN[m-1])

O valor de m é o comprimento da menor lista em seq.

Exemplos:

   1 >>> zip([1, 3, 5], [2, 4, 6])
   2 [(1, 2), (3, 4), (5, 6)]
   3 >>> zip([1, 3, 5], [2, 4, 6, 8, 10])
   4 [(1, 2), (3, 4), (5, 6)]

Exemplos Mais Complexos

Retirado do "Programming FAQ" em python.org, seção 2.12:

   1 # Primes < 1000
   2 print filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,
   3 map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000)))
   4 
   5 # First 10 Fibonacci numbers
   6 print map(lambda x,f=lambda x,f:(x<=1) or (f(x-1,f)+f(x-2,f)): f(x,f),
   7 range(10))
   8 
   9 # Mandelbrot set
  10 print (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,
  11 Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM,
  12 Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,
  13 i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y
  14 >=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr(
  15 64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy
  16 ))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24)
  17 #    \___ ___  \___ ___  |   |   |__ lines on screen
  18 #        V          V      |   |______ columns on screen
  19 #        |          |      |__________ maximum of "iterations"
  20 #        |          |_________________ range on y axis
  21 #        |____________________________ range on x axis

Magia negra? Concordo!