C'est assez difficile, 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 dans python pour produire les éléments suivants 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 à côté, comment l’implémentation changerait-elle s’il existe une dépendance entre foo et bar? Par exemple.
foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]
et print:
1.0 [2,3,4,5]
2.0 [1,3,4,5]
3.0 [1,2,4,5]
...
PS: Je sais le faire naïvement en utilisant if, des boucles et/ou des générateurs, mais j'aimerais apprendre à réaliser la même chose en utilisant des outils fonctionnels. S'agit-il simplement d'ajouter une instruction if à maptest? ou appliquer une autre carte de filtre aux barres en interne dans maptest?
Le moyen 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 fonction originale maptest
, vous pouvez également utiliser une fonction lambda dans map
:
map((lambda foo: maptest(foo, bars)), foos)
Êtes-vous familier avec d'autres langages fonctionnels? c’est-à-dire que vous essayez d’apprendre comment python fait de la programmation fonctionnelle ou essayez-vous de vous renseigner sur la programmation fonctionnelle et d’utiliser python comme véhicule?
De plus, comprenez-vous les compréhensions de liste?
map(f, sequence)
est directement équivalent (*) à:
[f(x) for x in sequence]
En fait, je pense qu'une fois map()
devait être supprimé de python 3.0 comme redondant (cela ne s'est pas produit).
map(f, sequence1, sequence2)
est principalement équivalent à:
[f(x1, x2) for x1, x2 in Zip(sequence1, sequence2)]
(il y a une différence dans la façon dont il gère le cas où les séquences ont une longueur différente. Comme vous l'avez vu, map()
ne remplit Aucune lorsque l'une des séquences s'épuise, alors que Zip()
s'arrête lorsque la séquence la plus courte arrête)
Donc, pour répondre à votre question spécifique, vous essayez de produire le résultat suivant:
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, suivie de barres:
def maptest(x):
print x, bars
map(maptest, foos)
Alternativement, vous pouvez créer une liste qui ressemble à ceci:
[bars, bars, bars, ] # etc.
et utilisez votre maptest original:
def maptest(x, y):
print x, y
Une façon de le faire serait de construire explicitement la liste au préalable:
barses = [bars] * len(foos)
map(maptest, foos, barses)
Alternativement, vous pouvez insérer le module itertools
. itertools
contient de nombreuses fonctions astucieuses qui vous aident à effectuer une programmation d'évaluation paresseuse de style fonctionnel en python. Dans ce cas, nous voulons itertools.repeat
, qui affichera son argument indéfiniment au fur et à mesure de votre itération. Ce dernier fait signifie que si vous faites:
map(maptest, foos, itertools.repeat(bars))
vous obtiendrez une sortie sans fin, puisque map()
continue tant qu'un des arguments produit toujours la sortie. Cependant, itertools.imap
ressemble à map()
, mais s’arrête dès que le plus court itérable s’arrête.
itertools.imap(maptest, foos, itertools.repeat(bars))
J'espère que cela t'aides :-)
(*) C'est un peu différent dans python 3.0. Là, map () renvoie essentiellement une expression génératrice.
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 (la partie [(x, bars) for x in foos]
) plutôt que d'utiliser map, car elle évite la surcharge d'un appel de fonction à chaque itération (ce qui peut être très important). Si vous ne l'utilisez que dans une boucle for, vous obtiendrez de meilleures vitesses 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 paresseusement chargée .
UPDATE En réponse à ce commentaire:
Bien sûr, vous savez que vous ne copiez pas de barres, toutes les entrées sont dans la même liste de barres. Ainsi, si vous en modifiez une (y compris les barres d'origine), vous les modifiez toutes.
Je suppose que c'est un point valable. Il y a deux solutions à cela auxquelles je peux penser. Le plus efficace est probablement quelque chose comme ceci:
tbars = Tuple(bars)
[(x, tbars) for x in foos]
Comme les n-uplets sont immuables, cela empêchera les barres d'être modifiées par les résultats de cette compréhension de liste (ou de compréhension du 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 coûter un peu cher en termes d’utilisation de la mémoire et de rapidité, je vous recommande donc de ne pas le faire, sauf si vous avez vraiment besoin d’ajouter à chacun d’eux.
La programmation fonctionnelle consiste à créer du code sans effets secondaires.
map est une abstraction de transformation de liste fonctionnelle. Vous l'utilisez pour prendre une séquence de quelque chose et le transformer en une séquence de quelque chose d'autre.
Vous essayez de l'utiliser comme un itérateur. Ne fais pas ça. :)
Voici un exemple d'utilisation de map pour construire la liste souhaitée. Il y a des solutions plus courtes (je n'utiliserais que des compréhensions), mais cela vous aidera à comprendre quelle carte fait un peu mieux:
def my_transform_function(input):
return [input, [1, 2, 3]]
new_list = map(my_transform, input_list)
Remarquez qu'à ce stade, vous ne faites 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 ne consiste pas à é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 secondaires et d'écrire moins de bugs.
>>> 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]
import itertools
foos=[1.0, 2.0, 3.0, 4.0, 5.0]
bars=[1, 2, 3]
print Zip(foos, itertools.cycle([bars]))
Voici un aperçu des paramètres de la fonction map(function, *sequences)
:
function
est le nom de votre fonction.sequences
est un nombre quelconque de séquences, qui sont généralement des listes ou des tuples. map
itérera sur eux simultanément et donnera les valeurs actuelles à function
. C'est pourquoi le nombre de séquences doit être égal au nombre de paramètres de votre fonction.On dirait que vous essayez d’itérer certains paramètres de function
mais que vous en maintenez d’autres constants. Malheureusement, map
ne prend pas cela en charge. J'ai trouvé ne ancienne proposition pour ajouter une telle fonctionnalité à Python, mais la construction de la carte est si propre et bien établie que je doute qu'un tel système puisse être implémenté.
Utilisez une solution de contournement telle que des variables globales ou des compréhensions de liste, comme d'autres l'ont suggéré.
Que dis-tu de ça:
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]*len(foos))
Est-ce que cela le ferait?
foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]
def maptest2(bar):
print bar
def maptest(foo):
print foo
map(maptest2, bars)
map(maptest, foos)