Quark! • Vendo Tópico - Python e a programação funcional
Python e a programação funcional
por 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
yields a function object. The unnamed object behaves like a function object defined with:
def name(arguments):
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:
>>> 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:
>>> dobro = lambda x: 2*x
>>> dobro(5)
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) :
>>> many_args = lambda x,y,z: (x*y)**z
>>> many_args(5,5,2)
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 , 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á:
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:
>>> map(lambda x: 2*x, [1, 2, 3, 4])
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.
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:
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:
>>> par = lambda x: x%2 == 0
>>> par(8)
>>> par(9)
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:
>>> filter( lambda x: x%2 == 0, [1,2,3,4,5,6,7,8,9,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:
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:
>>> reduce(lambda x,y: x+y , [x for x in range(1,11)])
Cá está, as várias funções explicadas em detalhe e bastantes exemplos ilustrativos. Qualquer questão, coloquem.
Re: Python e a programação funcional
por 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...
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.
por 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:
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 ) ter escrito:
reduce(lambda x,y: x+y , [elem for elem in range(1,11)])
que dá exactamente o mesmo resultado!
por 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?
por 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:
E que tal:
2*sum(vec)
por ruifm em Quinta Fev 16, 2012 9:37 pm
Tharis Escreveu: E que tal:
Não duvido que funcione!
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.
por 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.