Um pouco de funcional II

Continuando a série “Um pouco de Funcional“, chegou a hora de falar sobre map. Apesar de ser uma função originalmente de programação funcional, map hoje já está presente em várias linguagens (ver tabela).

A função map é utilizada para fazer mapeamentos em sequência.

Entenda sequência como qualquer objeto iterável.
ex.: listas, tuplas, strings, etc.

mapeamento

Imagem retirada do livro "Python para Desenvolvedores"

Mapeamento consiste em aplicar uma determinada função para cada elemento da sequência. A sua utilização em Python segue o seguinte modelo

map(funcao,seq1,seq2,seqn)

Para fins didáticos entenda que quanto as sequências são de tamanhos diferentes serão ‘preenchidas’ com None.

É possível simular o funcionamento de map com laço for da seguinte maneira

lista=list()
for x,y,z in zip(seq1,seq2,seqn):
	lista.append(funcao(x,y,z))

A função map independente do tipo de sequência, retorna uma lista com os resultados do seu mapeamento, porém, em Python 3k a função map retorna um iterator.

! map em Python 3k comporta-se como o itertools.imap()

>>> map(lambda x:x+1,range(3))
<map object at 0xb7c2b50c>

Mas se você realmente necessite que map retorne uma lista pode seguir três caminhos para consertar essa incompatibilidade.

1. Utilizar o construtor de lista na função map

#python3
>>> list(list(map(lambda x: x+1,range(4))))
[1,2,3,4]

2. Utilizar List Comprehension (melhor solução)

#python3
>>> [(lambda x:x+1)(x) for x in range(4)]
[1,2,3,4]

3. Reescrever o código para não precisar de uma lista. 🙂

>>> print ("FUUUUUUU",end="n")

Caso você esteja no Python 2.7 e queira testar esse novo comportamento do map

>>> from future_builtins import map

Como foi visto a utilização de map é bastante simples, e junto com lambda, reduce e filter torna-se uma ferramente bastante poderosa na resolução de vários problemas.

Abaixo segue algumas utilizações de map

>>> import re
>>> teste = ["2a","a3","c10",'1a2aaaaas']
>>> teste = map(lambda item: re.sub(r"[a-z]", "", item), teste)
['2', '3', '10', '12']
"".join(map(chr,[112, 121, 116, 104, 111, 110]))
'python'

Nos exemplos anteriores utilizei funções anônimas (lambda) com map, apenas por comodidade mas não há nenhuma restrição em utilizar funções definida por def

>>> def myPow(x,y):
	return x**y
>>> map(myPow,range(4),[2,3,4,5])
[0, 1, 16, 243]

Abaixo segue um exemplo de código utilizando as duas ferramentas já explicadas até aqui (lambda e map) .

#Gráfico de barras horizontal
#python 2.6.2
>>> from sys import stdout
>>> barras = lambda char,valores : map(lambda (valor,barra): stdout.write( "%2d"%valor+"  |"+ barra +"n"),map(lambda valor: (valor,valor*char),valores))
>>> barras("=",[2,3,10,3,1,0,7])
 2  |==
 3  |===
10  |==========
 3  |===
 1  |=
 0  |
 7  |=======
[None, None, None, None, None, None, None]

DICA: Na criação de códigos aninhados usando map, filter, reduce, lambda, etc sempre use a tática “dividir para conquistar”.

Agora vamos tentar explicar a função histograma, utilizando a dica anterior.

1. Cria-se uma lista de tuplas onde a primeira posição representa o valor e a segunda, a barra.

>>> valores = [2,3,10,3,1,0,7]
>>> char = "="
>>> map(lambda valor: (valor,valor*char),valores)
[(2, '=='), (3, '==='), (10, '=========='), (3, '==='), (1, '='), (0, ''), (7, '=======')]</p>

2. Aplica-se novamente o mapeamente com uma função que imprime na tela no formato  Valor  |  Barra

>>> from sys import stdout
>>> map(lambda (valor,barra): stdout.write( "%2d"%valor + "  |" + barra + "n"),[(2, '=='), (3, '==='),
(10, '=========='), (3, '==='), (1, '='), (0, ''), (7, '=======')])

3. Está quase tudo pronto mas ainda falta transformar tudo isso numa função

>>> from sys import stdout
>>> barras= lambda char,valores : map(lambda (valor,barra): stdout.write( "%2d"%valor+"  |"+ barra
+"n"),map(lambda valor: (valor,valor*char),valores))
>>> barras("=",[2,3,10,3,1,0,7])
 2  |==
 3  |===
10  |==========
 3  |===
 1  |=
 0  |
 7  |=======
[None, None, None, None, None, None, None]

Voilá !

Agora é necessário explicar duas coisas que talvéz tenham passado sem ser notado.

Por que além de imprimir na tela apareceu uma lista de None?

Bem, como tinha dito, sempre um mapeamento retorna uma lista com os valores da função aplicada em cada elemento, porém o sys.stdout.write é um método que não possou retorno. Logo, a lista de None é o retorno da função map.

! Funções sem retorno em Python, retornam None. 🙂

Por que usar sys.stdout.write ao invés de print?

Para responder isso teria que voltar a o último post e ver umas das regras na criação
de funções lambda.

  • O corpo da função deve ser uma expressão, não uma instrução.

Usar sys.stdout.write é uma forma de burlar essa restrição, o próprio print é definida usando o sys.stdout.write.

Até em Python 2.x print é uma instrução porém em Python 3k já é uma função. Isso fez-me fazer alguns testes:

#python2.6.2
>>> map(print,range(3))
SyntaxError: invalid syntax
>>> map(lambda x: print x,range(3))
SyntaxError: invalid syntax
#python3.0.1
>>> list(map(print,range(3)))
0
1
2
[None, None, None]
>>> list(map(lambda x: print(x),range(3)))
0
1
2
[None, None, None]

Como já era de esperar, com Python 3k não seria necessário usar o sys.stdout.write.

Ainda sobre o código do gráfico de barras, há a opção de juntar os dois lambdas em apenas um e assim aplicar o mapeamento apenas uma vez, vai ficar mas complicado que já está mas é uma opção.

Até o próximo post.

Links

http://docs.python.org/library/future_builtins.html#future_builtins.ascii
http://en.wikipedia.org/wiki/Map_(higher-order_function)
http://docs.python.org/library/functions.html#map
http://docs.python.org/tutorial/datastructures.html#functional-programming-tools
http://docs.python.org/release/3.0.1/whatsnew/3.0.html#views-and-iterators-instead-of-lists
http://ark4n.wordpress.com/python/
Rodrigo Lira

2  |==
3  |===
10  |==========
3  |===
1  |=
0  |
7  |=======



Warning: Missing argument 1 for cwppos_show_review(), called in /home/rodrigolira/blog.rodrigolira.net/wp-content/themes/flat/content-single.php on line 29 and defined in /home/rodrigolira/blog.rodrigolira.net/wp-content/plugins/wp-product-review/includes/legacy.php on line 18
  • Parabéns pelo post, Rodrigo!

    Uma ótima revisão de python funcional, inclusive mostrando algumas aplicações mais avançadas e o uso com Python 3!

    Aguardo mais posts,

    Marcel

    • Obrigado Marcel pelo apoio e divulgação.
      Pode aguarda que ainda tem muito por vir 🙂
      Ah, e a palestra vai sair sim!

      abração.

  • Muito boa a série! Valeu mesmo!

    • Valeu Rodrigo!
      Não esqueça de avisar quando vier pelo estado.

      abração