map(), reduce(), filter() e lambda

map()

map() é uma função builtin de Python, isto é, uma função que é implementada diretamente no interpretador Python, podendo ser utilizada sem a importação de um módulo específico. Essa função, em Python, serve para aplicarmos uma função a cada elemento de uma lista, retornando uma nova lista contendo os elementos resultantes da aplicação da função. Considere o exemplo abaixo:

>>> import math
>>> lista1 = [1, 4, 9, 16, 25]
>>> lista2 = map(math.sqrt, lista1)
>>> print lista2
[1.0, 2.0, 3.0, 4.0, 5.0]

Ao chamar a função map(math.sqrt, lista1), estamos solicitando ao interpretador para que execute a função math.sqrt (raiz quadrada – square root) usando como entrada cada um dos elementos de lista1, e inserindo o resultado na lista retornada como resultado da função map().

É uma forma bem interessante e expressiva de denotar a aplicação de uma função a cada elemento de uma lista (ou sequência). Mas, podemos facilmente substituir uma chamada a map() por list comprehensions. O código recém listado poderia ser substituído por:

>>> lista2 = [math.sqrt(x) for x in lista1]
>>> print lista2
[1.0, 2.0, 3.0, 4.0, 5.0]

O código acima produz o mesmo resultado que map(), pois, para cada elemento de lista1, executa a função math.sqrt e inclui o resultado dessa execução na lista de retorno.

O fato de a função map() ser tão facilmente substituída pelo uso de comprehensions, já criou até mesmo algumas discussões sobre manter ou não map() entre as funções builtin do Python 3000 [1].

reduce()

reduce() é outra função builtin do Python (deixou de ser builtin e passou a estar disponível no módulo functools a partir da versão 3000). Sua utilidade está na aplicação de uma função a todos os valores do conjunto, de forma a agregá-los todos em um único valor. Por exemplo, para aplicar a operação de soma a todos os elementos de uma sequência, de forma que o resultado final seja a soma de todos esses elementos, poderíamos fazer o seguinte:

>>> import operator #necessário para obter a função de soma
>>> valores = [1, 2, 3, 4, 5]
>>> soma = reduce(operator.add, valores)
>>> print soma
15

É claro que, para realizar a soma de todos os elementos de uma sequência, é muito mais claro utilizarmos a função sum():

>>> print sum(valores)
15

Como falei anteriormente, reduce() foi retirada do conjunto de builtins de Python, em parte devido à obscuridade que pode acabar gerando [1].

filter()

Como o próprio nome já deixa claro, filter() filtra os elementos de uma sequência. O processo de filtragem é definido a partir de uma função que o programador passa como primeiro argumento da função. Assim, filter() só “deixa passar” para a sequência resultante aqueles elementos para os quais a chamada da função que o usuário passou retornar True. Vejamos um exemplo:

>>> def maior_que_zero(x):
...     return x > 0
...
>>> valores = [10, 4, -1, 3, 5, -9, -11]
>>> print filter(maior_que_zero, valores)
[10, 4, 3, 5]

No exemplo acima, filter() chamou a função maior_que_zero para cada um dos elementos contidos em valores. Se a função retornar True, o elemento é inserido na lista de resultado. Caso contrário, não o é. Ou seja, é feita a filtragem e só passam aqueles elementos que são maiores que zero.

Assim, como no exemplo da builtin map(), aqui também podemos escrever com facilidade uma comprehension com a mesma funcionalidade:

>>> print [x for x in valores if x > 0]
[10, 4, 3, 5]

Devido a essa fácil substituição, filter() também já esteve na mira para ser retirada do conjunto de builtins, embora tenha acabado permanecendo.

lambda

No exemplo da função filter(), tivemos que definir uma nova função (chamada maior_que_zero) para usar somente dentro da função filter(), sendo chamada uma vez para cada elemento. Ao invés de definir uma nova função dessa forma, poderíamos definir uma função válida somente enquanto durar a execução do filter. Não é necessário nem dar um nome a tal função, sendo portanto chamada de função anônima ou função lambda. Considere o exemplo abaixo:

>>> valores = [10, 4, -1, 3, 5, -9, -11]
>>> print filter(lambda x: x > 0, valores)
[10, 4, 3, 5]

Definimos uma função anônima (portanto, não tem nome), que recebe uma entrada (a variável x) e retorna o resultado da operação x > 0, True ou False.

Poderíamos também usar uma função lambda no exemplo da função reduce():

>>> valores = [1, 2, 3, 4, 5]
>>> soma = reduce(lambda x, y: x + y, valores)
>>> print soma
15

No código acima, definimos uma função anônima que recebe duas entradas e retorna a soma dessas entradas.

[1] Guido Van Rossum. The fate of reduce() in Python 3000

14 comentários sobre “map(), reduce(), filter() e lambda

  1. Pingback: all() e any() « Python Help
  2. Pingback: Brincando com Listas | Python Help
  3. Pingback: Ordenação de uma lista | Python Help
  4. Olá, eu estou com uma dificuldade numa parte de um software que estou fazendo… Eu tenho uma lista com cédulas e moedas e preciso encontrar uma soma usando as menores cédulas ou moedas possiveis que resulte num determinado valor…
    Por exemplo:

    O valor é 20… Entao o software tem que me “dizer” para usar apenas a cédula de 20 reais daquela lista
    O valor é 10.50… Usar apenas cédula de 10 reais e uma moeda de 50 centavos
    O valor é 50… Usar apenas uma cédula de 50 reais e nao 20+20+10 por exemplo…

    Estou aprendendo a desenvolver em python agora por isso tenho alguma dificuldade na parte de algoritmos etc… Agradeço desde já, abraços

  5. Pingback: Funções como objetos de primeira classe | Python Help
  6. Pingback: Como funcionam as listas de Python? | Python Help
  7. Pingback: Como funcionam as listas de Python? - Peguei do
  8. Quanto a questão do porque funções filter e map não dão lugar a list comprehensions é simplesmente por uma questão de desempenho.

    Eu mensurei o tempo de execução dos códigos abaixo usando o comando mágico do ipython %%timeit -n 1000000:

    FILTER

    valores = range(200)

    def op1():
    filter(lambda x: x > 0, valores)

    def op2():
    [x for x in valores if x > 0]

    A diferença foi:
    op1 – 280 nanosegundos
    op2 – 9.400 nanosegundos ou 9,4 microsegundos

    A funçao op1 é quase 34 vezes mais rápida que a op2.

    MAP

    valores = range(200)

    def map1():
    map(math.sqrt, valores)

    def map2():
    [math.sqrt(x) for x in valores]

    A diferença foi:
    map1 – 277 nanosegundos
    map2 – 34.400 nanosegundos ou 34,4 microsegundos

    A função map1 é 124 vezes mais rápida que a map2.

    Se alguém ai usa muitas list comprehensions e precisa manipular grandes massas de dados, aconselho dar lugar a map e filter.

    • Fiz um teste rápido:

      # == Name script: tsec.py ==

      # nsec = 1.10**-9 (ou 1 bilhão de nsec = 1 segundos)
      # usec = 1.10**-6 (ou 1 milhão de usec = 1 segundos)
      # msec = 1.10**-3 (ou 1 mil de msec = 1 segundos)

      import math

      valores = range(10000)

      def filt():
      return filter(lambda x: x > 0, valores)

      def list_comp():
      return [x for x in valores if x > 0]

      def gen_exp():
      return (x for x in valores if x > 0)

      def maps():
      return map(math.sqrt, valores)

      def list_comp2():
      return [math.sqrt(x) for x in valores]

      def gen_exp2():
      return (math.sqrt(x) for x in valores)

      def relative():
      return [(math.sqrt(x) for x in valores)]

      “””
      Rodando em um terminal linux:

      python -mtimeit -s’import tsec’ ‘tsec.op1()’
      python -mtimeit -s’import tsec’ ‘tsec.list_comp()’
      python -mtimeit -s’import tsec’ ‘tsec.gen_exp()’
      python -mtimeit -s’import tsec’ ‘tsec.maps()’
      python -mtimeit -s’import tsec’ ‘tsec.list_comp2()’
      python -mtimeit -s’import tsec’ ‘tsec.gen_exp2()’
      python -mtimeit -s’import tsec’ ‘tsec.relative()’

      Resultados:
      500000 loops, best of 5: 402 nsec per loop (op1)
      500 loops, best of 5: 731 usec per loop (list_comp)
      500000 loops, best of 5: 585 nsec per loop (gen_exp)
      500000 loops, best of 5: 449 nsec per loop (maps)
      200 loops, best of 5: 1.81 msec per loop (list_comp2)
      500000 loops, best of 5: 564 nsec per loop (gen_exp2)
      500000 loops, best of 5: 613 nsec per loop (relative)

      “””

      Lembrando que listComp gera na memória RAM, e genExp gera na hora do uso (processador ), então normalmente genExp é melhor para uso único ou largo, mas para múltiplos usos de uma mesma list listComp é melhor, pois é gerada uma única vez.

Deixar mensagem para andreasd6 Cancelar resposta