Página 1 de 1

Python e a programação funcional

MensagemEnviado: Segunda Jul 18, 2011 10:44 am
por Bruno Oliveira
A função lambda é talvez a instrução mais conhecida de programação funcional, já que aparece em todas as linguagens que suportam este paradigma, como Lisp e outras.

No entanto, esta função lambda (que se insere no que se chama untyped lambda calculus) não tem qualquer relação com a função lambda formalmente definida em Matemática, e o uso que lhe é dado em Programação também é relativo, uma vez que se podem definir funções standard e escrever programas que estejam estruturados segundo o paradigma imperativo e façam exactamente a mesma coisa. As vantagens de usar lambda functions aparecem quando se querem escrever por uma ou outra razão programas com códigos mais curtos e compactos e usar menos memória (embora isto quase nunca seja um problema se se pensar o suficiente no algoritmo que estamos a usar para resolver o problema. ).

É também importante dizer aqui, que por si só, as lambda functions têm muito pouca ou nenhuma utilidade, sendo a sua utilidade revelada quando estas operam sobre listas por compreensão e são combinadas com outras funções de programação funcional, como o reduce, o map e o filter.

Depois deste paleio todo, e passando à prática, vou transcrever aqui a documentação oficial do Python na parte em que se referem as funções lambda:

"
Lambda forms (lambda expressions) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression
Código: Seleccionar Todos
lambda arguments: expression
yields a function object. The unnamed object behaves like a function object defined with:

Código: Seleccionar Todos
def name(arguments):
    return expression


"
As funções lambda são anónimas pois ao ler acima, o function object que se refere acima não tem nenhum nome associado a ele. (novamente, a wikipedia pode explicar melhor este ponto melhor que eu, pois é daquelas coisas para as quais a pessoa está "formatada" para perceber, e torna-se um pouco dificil explicar estes fundamentos teóricos por palavras, sendo melhor ver exemplos.)

Portanto, passando agora a exemplos, vou tentar explicar e ilustrar 4 conceitos bastante usados em programação funcional, que são os que eu conheço:

[*] Lambda functions;

[*] A função map;

[*] A função filter;

[*] A função reduce;

Estas quatro funções, quando combinadas entre si, podem produzir alguns resultados interessantes e são uma excelente forma para obrigar a pensar out of the box. :)

Funções Lambda

As funções lambda criam objectos que não ficam agarrados a nenhum identificador, o que te obriga a declará-lo se as quiseres usar de forma isolada. (ou seja, tens de escrever o name da função, como está no exemplo acima.) Se não o declarares, apenas verás na Python shell a confirmação da criação de um objecto devido à declaração da função lambda, se esta não tiver erros de sintaxe:

Código: Seleccionar Todos
>>> lambda x: 2*x
<function <lambda> at 0x2381f50>
>>>


Deverão obter uma coisa semelhante nos vossos pc's. Assim, para se poderem usar as funções lambda, como se usam as funções normais, tem de se lhes atribuir um identificador:

Código: Seleccionar Todos
>>> dobro = lambda x: 2*x
>>> dobro(5)
10
>>>


E creio que em termos práticos é apenas isto que é preciso ter presente para perceber a declaração e uso de lambda functions. Também podes ter funções lambda a receberem muitos argumentos, que são colocados na chamada da função, pela ordem em que são declarados, assim a minha função many_args, para ser chamada, teria a configuração, many_args(x, y, z) :

Código: Seleccionar Todos
>>> many_args = lambda x,y,z: (x*y)**z
>>> many_args(5,5,2)
625
>>>


Função map

A função map, e todas as outras de que tratarei em seguida, podem ser usadas juntamente com as funções lambda, mas apenas quando estas são aplicadas a listas por compreensão, ou outras.

Quando digo aqui aplicadas a listas, refiro-me a usar as funções lambda, não como as funções standard definidas com a keyword def, mas sim como uma função que modifica os conteúdos de uma lista, operando sobre ela. É importante referir que é apenas com as funções anónimas que se podem conseguir estes resultados de modificação de conteúdo de listas, que são muito úteis.

Imagine-se que temos uma lista, que contém os 4 primeiros números naturais, e que queremos agora duplicar cada elemento dessa lista. É óbvio que o que temos de fazer, é aplicar a função f(x) = 2\,x, a todos os elementos da lista. No entanto, o Python gere a memória mantendo apenas referências a listas já criadas, e não realiza operações directamente sobre elas, pelo que ao fazeres uma função normal, que te devolva o dobro de um número x, e lhe passares a nossa lista como argumento, o resultado será:

Código: Seleccionar Todos
[1, 2, 3, 4, 1, 2, 3, 4]


que não é o que nós queremos de todo!!

A função map, resolve o problema:

Basicamente, como o nome diz, existe um mapping ou uma correspondência, entre cada elemento da lista e a função lambda responsável por alterar essa mesma lista. O nosso problema resolve-se num instante:

Código: Seleccionar Todos
>>> map(lambda x: 2*x, [1, 2, 3, 4])
[2, 4, 6, 8]
>>>


Portanto aqui estão outra vez, as funções lambda usadas para realizar operações sobre listas, e fica introduzido o conceito da função map.

Função filter

A função filter, é muito simples de perceber, mas talvez mais díficil de interligar em expressões muito complicadas do que as outras já referidas até aqui. O que a função filter faz, é, tal como o nome diz, filtrar os conteúdos de uma dada lista caso estes verifiquem (ou não) a imposição de uma dada função lambda, que pode dar jeito ser uma função booleana, ou seja, uma função que retorna apenas dois valores: True ou False, caso o argumento verifique ou não a condição imposta na função. Já foi aqui discutido que, em Programação, uma maneira útil de saber se um número é ou não par, pode ser avaliando o resto da divisão desse número por 2. Se o resto for zero, o número é par, caso contrário não é. A condição de paridade de um número x, é dada então por:
Código: Seleccionar Todos
x%2 == 0
.

Isto pode ser metido no meio de uma função lambda, ficando a função a funcionar como uma função booleana:

Código: Seleccionar Todos
>>> par = lambda x: x%2 == 0
>>> par(8)
True
>>> par(9)
False
>>>


Assim sendo, tendo uma lista com os dez primeiros números naturais, para ficarmos apenas com os números pares, ou para "filtrarmos" os pares, basta-nos fazer:

Código: Seleccionar Todos
>>> filter( lambda x: x%2 == 0, [1,2,3,4,5,6,7,8,9,10] )
[2, 4, 6, 8, 10]
>>>


E é esta a explicação mais simples da função filter.

Função reduce

A função reduce, que já foi vista anteriormente, utiliza-se, como eu disse (informalmente) atrás, para "gastar" os elementos de uma lista, até que esta seja apenas reduzida a um único elemento. No entanto, em Programação, pode dar jeito conhecer as definições que seguem os padrões da linguagem que estamos a usar, para podermos usar estas funções que estão directamente implementadas na linguagem (as chamadas built-in functions) da melhor forma possível.

Assim sendo, de maneira formal, a função reduce é uma built-in function que recebe uma função lambda de dois argumentos e a aplica cumulativamente aos itens de uma estrutura de dados iterável, de modo a que esta seja reduzida a um único elemento. As únicas estruturas de dados iteráveis que vimos até aqui, foram as listas (na realidade, podemos também iterar sobre os caracteres de uma string, mas esta não constitui uma estrutura de dados).

Como exemplo, imagine-se uma lista com os 10 primeiros números, e que os queremos somar a todos. Para isso, temos então de criar uma função lambda que denote a operação de soma, que, como vês, tem de receber forçosamente 2 argumentos diferentes, os operandos:

Código: Seleccionar Todos
lambda x, y: x+y


Depois, segue-se o idioma usado até aqui, criamos uma lista por compreensão que contenha os 10 primeiros números naturais, e somamo-los usando a função lambda para soma, e passando-a (juntamente com a lista) como argumentos da função reduce:

Código: Seleccionar Todos
>>> reduce(lambda x,y: x+y , [x for x in range(1,11)])
55
>>>


Cá está, as várias funções explicadas em detalhe e bastantes exemplos ilustrativos. Qualquer questão, coloquem.

Cumprimentos,
Bruno

Re: Python e a programação funcional

MensagemEnviado: Quarta Fev 15, 2012 7:26 pm
por ruifm
Muito útil mesmo!
já usei o map e o filter para reduzir substâncialmente o tempo que os meus programas demoravam a encontrar a solução. Antes tinha de trabalhar com index's, criar listas novas so para por la os valores que me interessavam...
Not anymore!

Tenho 2 perguntas:
1-quando usamos a funçao map, podemos usa-la sem lambda? do genero map(func(x),lista_exemplo)

2-Não percebi o teu exemplo no reduce. Tu requeres um argumento y, mas depois nunca lhe atribuis um valor.

Re: Python e a programação funcional

MensagemEnviado: Quarta Fev 15, 2012 7:39 pm
por Bruno Oliveira
1- Podes, desde que tenhas definido antes dessa linha de código a função func. E usa-la assim:

Código: Seleccionar Todos
map(func, list)


2 - Na realidade, os argumentos x,y usados na definição da função lambda, apenas lá estão para denotar a operação de soma de todos os elementos da lista, pelo que um x, nada tem que ver com o outro. Eu também poderia (deveria :P ) ter escrito:

Código: Seleccionar Todos
reduce(lambda x,y: x+y , [elem for elem in range(1,11)])


que dá exactamente o mesmo resultado!

Deu para perceber? :roll:

Cumprimentos,
Bruno

Re: Python e a programação funcional

MensagemEnviado: Quarta Fev 15, 2012 7:50 pm
por ruifm
basicamente a funçao reduce funcionou como sum, nesse caso.
a vantagem do reduce é portanto, a de efectuarmos uma operaçao qualquer com todos os elementos de uma lista, sem que essa operaçao seja necessariamente a soma.

isto pode ser ousado, mas daria para por exemplo, implementar o seguinte na funcao reduce:
reduce(lambda x,y: 2*x+y, elemento for elemento in range(1,11))

para duplicar cada elemento e soma-lo?

Re: Python e a programação funcional

MensagemEnviado: Quarta Fev 15, 2012 8:59 pm
por Tharis
ruifm Escreveu:isto pode ser ousado, mas daria para por exemplo, implementar o seguinte na funcao reduce:
reduce(lambda x,y: 2*x+y, elemento for elemento in range(1,11))

para duplicar cada elemento e soma-lo?


E que tal:

Código: Seleccionar Todos
2*sum(vec)

Re: Python e a programação funcional

MensagemEnviado: Quinta Fev 16, 2012 9:37 pm
por ruifm
Tharis Escreveu:E que tal:

Código: Seleccionar Todos
2*sum(vec)

Não duvido que funcione! :lol:
Queria era saber se posso fazer esse tipo de maluquices usando a funçao reduce. Parece dar jeito por exemplo para listar produtos de digitos de numeros, para somar primos, etc.

Re: Python e a programação funcional

MensagemEnviado: Quinta Fev 16, 2012 10:15 pm
por Tharis
ruifm Escreveu:Queria era saber se posso fazer esse tipo de maluquices usando a funçao reduce. Parece dar jeito por exemplo para listar produtos de digitos de numeros, para somar primos, etc.


Podes, mas para somar qualquer iterável que seja, tens a função sum:

Código: Seleccionar Todos
>>> print sum(map(int, str(592939)))
37