Python e a programação funcional

Secção dedicada à linguagem de programação favorita dos quarkianos: Python!

Python e a programação funcional

Mensagempor Bruno Oliveira em Segunda Jul 18, 2011 10:44 am

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
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Python e a programação funcional

Mensagempor ruifm em Quarta Fev 15, 2012 7:26 pm

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.
"Everything is determined, the beginning as well as the end, by forces over which we have no control." - Albert Einstein
Avatar do utilizador
ruifm
down-Quark!
down-Quark!
 
Mensagens: 205
Registado: Segunda Jan 16, 2012 12:04 am
Localização: Lisboa - IST

Re: Python e a programação funcional

Mensagempor Bruno Oliveira em Quarta Fev 15, 2012 7:39 pm

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
e^{ix}=cos x + i\,sin x
Avatar do utilizador
Bruno Oliveira
top-Quark!
top-Quark!
 
Mensagens: 1553
Registado: Quarta Nov 14, 2007 10:19 pm
Localização: Lisboa

Re: Python e a programação funcional

Mensagempor ruifm em Quarta Fev 15, 2012 7:50 pm

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?
"Everything is determined, the beginning as well as the end, by forces over which we have no control." - Albert Einstein
Avatar do utilizador
ruifm
down-Quark!
down-Quark!
 
Mensagens: 205
Registado: Segunda Jan 16, 2012 12:04 am
Localização: Lisboa - IST

Re: Python e a programação funcional

Mensagempor Tharis em Quarta Fev 15, 2012 8:59 pm

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)
Avatar do utilizador
Tharis
up-Quark!
up-Quark!
 
Mensagens: 387
Registado: Quinta Out 23, 2008 4:26 pm

Re: Python e a programação funcional

Mensagempor ruifm em Quinta Fev 16, 2012 9:37 pm

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.
"Everything is determined, the beginning as well as the end, by forces over which we have no control." - Albert Einstein
Avatar do utilizador
ruifm
down-Quark!
down-Quark!
 
Mensagens: 205
Registado: Segunda Jan 16, 2012 12:04 am
Localização: Lisboa - IST

Re: Python e a programação funcional

Mensagempor Tharis em Quinta Fev 16, 2012 10:15 pm

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
Avatar do utilizador
Tharis
up-Quark!
up-Quark!
 
Mensagens: 387
Registado: Quinta Out 23, 2008 4:26 pm


Voltar para Pitónica

Quem está ligado

Utilizadores a navegar neste fórum: Nenhum utilizador registado e 1 visitante