127 votes

Comment utiliser python map et d'autres outils fonctionnels

C'est un peu n00bish, mais j'essaie d'apprendre/comprendre la programmation fonctionnelle en python. Le code suivant :

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, bars)

produit :

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

Q. Existe-t-il un moyen d'utiliser map ou tout autre outil fonctionnel en python pour produire ce qui suit sans boucles, etc.

1.0 [1,2,3]
2.0 [1,2,3]
3.0 [1,2,3]
4.0 [1,2,3]
5.0 [1,2,3]

Juste à titre d'information, comment la mise en œuvre changerait-elle s'il existe une dépendance entre foo et bar ?

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]

et imprimer :

1.0 [2,3,4,5]
2.0 [1,3,4,5]
3.0 [1,2,4,5]
...

**P.S : Je sais comment le faire naïvement en utilisant des if, des boucles et/ou des générateurs, mais j'aimerais apprendre comment réaliser la même chose en utilisant des outils fonctionnels. Est-ce qu'il suffit d'ajouter une instruction if à maptest ou d'appliquer une autre carte de filtre aux barres en interne dans maptest ? **

194voto

John Fouhy Points 14700

Connaissez-vous d'autres langages fonctionnels ? En d'autres termes, essayez-vous d'apprendre comment python fait de la programmation fonctionnelle, ou essayez-vous d'apprendre la programmation fonctionnelle en utilisant python comme véhicule ?

De plus, comprenez-vous les compréhensions de listes ?

map(f, sequence)

est directement équivalent (*) à :

[f(x) for x in sequence]

En fait, je pense map() devait être supprimé de python 3.0 en raison de sa redondance (ce qui ne s'est pas produit).

map(f, sequence1, sequence2)

est en grande partie équivalent à :

[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]

(il y a une différence dans la façon dont il traite le cas où les séquences sont de longueur différente. Comme vous l'avez vu, map() remplit None quand une des séquences s'épuise, alors que zip() s'arrête lorsque la séquence la plus courte s'arrête)

Donc, pour répondre à votre question spécifique, vous essayez de produire le résultat :

foos[0], bars
foos[1], bars
foos[2], bars
# etc.

Vous pouvez le faire en écrivant une fonction qui prend un seul argument et l'imprime, suivi de barres :

def maptest(x):
     print x, bars
map(maptest, foos)

Vous pouvez également créer une liste qui ressemble à ceci :

[bars, bars, bars, ] # etc.

et utiliser votre maptest original :

def maptest(x, y):
    print x, y

Une façon d'y parvenir serait d'établir explicitement la liste au préalable :

barses = [bars] * len(foos)
map(maptest, foos, barses)

Alternativement, vous pouvez tirer dans le itertools module. itertools contient de nombreuses fonctions astucieuses qui vous permettent de faire de la programmation paresseuse de style fonctionnel en python. Dans ce cas, nous voulons itertools.repeat qui affichera indéfiniment son argument au fur et à mesure que vous l'itérerez. Ce dernier fait signifie que si vous faites :

map(maptest, foos, itertools.repeat(bars))

vous obtiendrez une sortie sans fin, puisque map() continue tant que l'un des arguments produit encore des résultats. Cependant, itertools.imap c'est comme map() mais s'arrête dès que l'itérable le plus court s'arrête.

itertools.imap(maptest, foos, itertools.repeat(bars))

J'espère que cela vous aidera :-)

(*) C'est un peu différent dans python 3.0. Là-bas, map() renvoie essentiellement une expression génératrice.

55voto

sth Points 91594

Le plus simple serait de ne pas passer bars à travers les différentes fonctions, mais d'y accéder directement à partir de maptest :

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo):
    print foo, bars

map(maptest, foos)

Avec votre original maptest vous pouvez également utiliser une fonction lambda dans la fonction map :

map((lambda foo: maptest(foo, bars)), foos)

30voto

Jason Baker Points 56682

Voici la solution que vous recherchez :

>>> foos = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> bars = [1, 2, 3]
>>> [(x, bars) for x in foos]
[(1.0, [1, 2, 3]), (2.0, [1, 2, 3]), (3.0, [1, 2, 3]), (4.0, [1, 2, 3]), (5.0, [
1, 2, 3])]

Je recommanderais d'utiliser une compréhension de liste (l'option [(x, bars) for x in foos] ) plutôt que d'utiliser map, car il évite l'overhead d'un appel de fonction à chaque itération (qui peut être très important). Si vous ne l'utilisez que dans une boucle for, vous obtiendrez de meilleurs résultats en utilisant un générateur de compréhension :

>>> y = ((x, bars) for x in foos)
>>> for z in y:
...     print z
...
(1.0, [1, 2, 3])
(2.0, [1, 2, 3])
(3.0, [1, 2, 3])
(4.0, [1, 2, 3])
(5.0, [1, 2, 3])

La différence est que la compréhension du générateur est chargé paresseusement .

UPDATE En réponse à ce commentaire :

Bien sûr, vous savez que vous ne copiez pas les barres, toutes les entrées sont la même liste de barres. Donc si vous modifiez l'une d'entre elles (y compris les barres originales), vous les modifiez toutes.

Je suppose que c'est un point valable. Je pense qu'il y a deux solutions à ce problème. La plus efficace est probablement quelque chose comme ceci :

tbars = tuple(bars)
[(x, tbars) for x in foos]

Comme les tuples sont immuables, cela empêchera les barres d'être modifiées par les résultats de cette compréhension de liste (ou de générateur si vous choisissez cette voie). Si vous avez vraiment besoin de modifier chacun des résultats, vous pouvez le faire :

from copy import copy
[(x, copy(bars)) for x in foos]

Cependant, cela peut s'avérer un peu coûteux, tant en termes d'utilisation de la mémoire que de vitesse, et je vous le déconseille donc, à moins que vous n'ayez vraiment besoin d'en ajouter à chacun d'entre eux.

20voto

Dustin Points 35205

La programmation fonctionnelle consiste à créer un code sans effets secondaires.

map est une abstraction fonctionnelle de transformation de liste. Vous l'utilisez pour prendre une séquence de quelque chose et la transformer en une séquence d'autre chose.

Vous essayez de l'utiliser comme un itérateur. Ne faites pas ça. :)

Voici un exemple de la façon dont vous pourriez utiliser map pour établir la liste que vous souhaitez. Il existe des solutions plus courtes (j'utiliserais simplement les compréhensions), mais cela vous aidera à comprendre un peu mieux ce que fait map :

def my_transform_function(input):
    return [input, [1, 2, 3]]

new_list = map(my_transform, input_list)

Remarquez qu'à ce stade, vous n'avez fait qu'une manipulation de données. Maintenant vous pouvez l'imprimer :

for n,l in new_list:
    print n, ll

-- Je ne suis pas sûr de ce que vous entendez par "sans boucles". fp n'a pas pour but d'éviter les boucles (vous ne pouvez pas examiner tous les éléments d'une liste sans les visiter tous). Il s'agit d'éviter les effets de bord, et donc d'écrire moins de bugs.

12voto

Roberto Bonvallet Points 6336
>>> from itertools import repeat
>>> for foo, bars in zip(foos, repeat(bars)):
...     print foo, bars
... 
1.0 [1, 2, 3]
2.0 [1, 2, 3]
3.0 [1, 2, 3]
4.0 [1, 2, 3]
5.0 [1, 2, 3]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X