2010-04-14 8 views
30

Est-il possible de définir une compréhension de liste récursive en Python?Compréhension de liste récursive en Python?

Peut-être un exemple simpliste, mais quelque chose le long des lignes de:

nums = [1, 1, 2, 2, 3, 3, 4, 4] 
willThisWork = [x for x in nums if x not in self] # self being the current comprehension 

est quelque chose comme ça possible?

+4

Si l'ordre n'est pas un problème, 'list (set (num))'. Sinon, vérifiez 'unique_everseen' dans http://docs.python.org/library/itertools.html. – kennytm

+0

alerte fuite d'abstraction. À mon avis, les compréhensions ne doivent pas être considérées comme des boucles, même si elles peuvent être implémentées comme des boucles dans cpython. – wim

Répondre

30

Non, il n'y a pas de (documenté, solide, stable, ...;) moyen de se référer à "la compréhension actuelle". Vous pouvez simplement utiliser une boucle:

res = [] 
for x in nums: 
    if x not in res: 
    res.append(x) 

Bien sûr, cela est très coûteux (O (N carré)), de sorte que vous pouvez l'optimiser avec un set auxiliaire (je suppose que le maintien de l'ordre des éléments dans res conforme à celle des éléments de nums, sinon set(nums) vous ferait; -) ...:

res = [] 
aux = set() 
for x in nums: 
    if x not in aux: 
    res.append(x) 
    aux.add(x) 

c'est énormément plus rapide pour les listes très longues (O (N) au lieu de N au carré).

Modifier: en Python 2.5 ou 2.6, vars()['_[1]'] pourrait effectivement travailler dans le rôle que vous voulez pour self (pour un listcomp non imbriqué) ... ce qui est la raison pour laquelle je me suis qualifié ma déclaration en précisant qu'il n'y a pas documenté, solide, stable façon d'accéder à "la liste en cours de construction" - ce "nom" particulier et non documenté '_[1]' (délibérément choisi pour ne pas être un identifiant valide ;-) est le sommet des "artefacts d'implémentation" et de tout code qui en dépend mérite d'être mis hors de sa misère ;-).

+1

Les opérations set en font O (n log (n)), j'en suis presque sûr. –

+2

@ Les ensembles dash-tom-bang en Python ne sont pas implémentés en tant qu'arbres rouge-noir (comme en STL), mais utilisent plutôt des hachages pour autant que je sache, donc ce sera O (N). –

+1

@Justin est correct - les ensembles python et les dicts sont des hachages bien optimisés, avec O (1) coût amorti pour l'ajout d'éléments et O (1) coût pour les recherches. –

2

no. cela ne fonctionnera pas, il n'y a pas de self pour se référer à la compréhension de la liste en cours d'exécution.

Et la raison principale est bien sûr que les listes de compréhension n'ont pas été conçues pour cet usage.

1

Je ne sais pas si c'est ce que vous voulez, mais vous pouvez écrire la liste imbriquées compréhensions:

xs = [[i for i in range(1,10) if i % j == 0] for j in range(2,5)] 
assert xs == [[2, 4, 6, 8], [3, 6, 9], [4, 8]] 

A partir de votre exemple de code, vous semblez vouloir simplement éliminer les doublons, que vous pouvez faire avec des jeux:

xs = sorted(set([1, 1, 2, 2, 3, 3, 4, 4])) 
assert xs == [1, 2, 3, 4] 
1

n °

Mais il semble que vous essayez de faire une liste des éléments uniques dans nums.

Vous pouvez utiliser un set:

unique_items = set(nums) 

Notez que les éléments en nums doivent être hashable.

Vous pouvez également effectuer les opérations suivantes. Ce qui est proche comme je peux obtenir à votre idée originale. Mais ce n'est pas aussi efficace que de créer un set.

unique_items = [] 
for i in nums: 
    if i not in unique_items: 
     unique_items.append(i) 
2

Pour ce faire:

nums = [1, 1, 2, 2, 3, 3, 4, 4] 
set_of_nums = set(nums) 
unique_num_list = list(set_of_nums) 

ou même ceci:

unique_num_list = sorted(set_of_nums) 
+0

Les compréhensions de liste sont inutiles. 'unique_num_list = liste (set_of_nums)'. 'trié (set_of_nums)' renvoie une liste. –

+0

@PreludeAndFugue: les bons points. Je vais changer le code. – hughdbrown

8

En fait, vous pouvez! Cet exemple avec une explication nous l'espérons illustrera comment.

Définir un exemple récursif pour obtenir un nombre seulement quand il est 5 ou plus et si ce n'est pas, l'incrémenter et appeler à nouveau la fonction 'vérifier'. Répétez ce processus jusqu'à ce qu'il atteigne 5 à quel point le retour 5.

print [ (lambda f,v: v >= 5 and v or f(f,v+1))(lambda g,i: i >= 5 and i or g(g,i+1),i) for i in [1,2,3,4,5,6] ] 

Résultat:

[5, 5, 5, 5, 5, 6] 
>>> 

essentiellement les deux fonctions anonymes interagissent ainsi:

let f(g,x) = { 
       expression, terminal condition 
       g(g,x), non-terminal condition 
      } 

let g(f,x) = { 
       expression, terminal condition 
       f(f,x), non-terminal condition 
      } 

make g, f la même fonction sauf que dans l'un ou les deux ajouter une clause où le paramètre est modifié de manière à provoquer l'atteinte de la condition terminale, puis aller f (g, x) de cette façon g devenir est une copie de f ce qui en fait comme:

f(g,x) = { 
       expression, terminal condition 
       { 
        expression, terminal condition, 
        g(g,x), non-terminal codition 
       }, non-terminal condition 
      } 

Vous devez faire cela parce que vous ne pouvez pas accéder à la fonction anonyme lui-même après avoir été exécutée.

i.e.

(lambda f,v: somehow call the function again inside itself)(_,_) 

donc dans cet exemple, soit A = la première fonction et la deuxième chambre. Nous appelons A passant B comme f et i comme v. Maintenant que B est essentiellement une copie de A et c'est un paramètre qui a été passé, vous pouvez maintenant appeler B qui est comme appelant A.

Ceci génère les factorielles dans un liste

print [ (lambda f,v: v == 0 and 1 or v*f(f,v-1))(lambda g,i: i == 0 and 1 or i*g(g,i-1),i) for i in [1,2,3,5,6,7] ] 

[1, 2, 6, 120, 720, 5040] 
>>> 
+2

Le clonage du lambda est inutile; vous pouvez utiliser un proxy générique comme premier lambda pour permettre à n'importe quel type de deuxième lambda de s'appeler lui-même. '(lambda f, arg: f (f, arg)) (lambda soi, v: ...., première valeur)' –

Questions connexes