2010-03-01 25 views
8

Le message suivant apparaît dans mon code Python 2.6:combinaison truand d'expression du générateur avec pour boucle

for src, dst in ([s,d] for s in universe for d in universe if s != d): 

Puis-je faire beaucoup mieux? Ce que je n'aime pas particulièrement, c'est que je spécifie deux fois la même paire, une fois pour la boucle for et encore pour l'expression du générateur. Je ne suis pas certain de préférer:

for src, dst in itertools.product(universe, universe): 
    if src != dst: 

Existe-t-il un moyen d'exprimer cette boucle de manière concise?

universe arrive à être une liste, si cela fait une différence. L'ordre d'itération n'a pas d'importance.

+0

Quel type de routine nécessite/permet un produit croisé comme celui-ci? –

+0

Je pense que votre code est sympa, le premier exemple est plus clair à comprendre que l'autre. – dalloliogm

+0

@Ignacio: Je suis en train de construire un graphe à deux voies dont les nœuds sont les éléments de 'universe', et qui sera traité par un autre logiciel ailleurs qui attend un ensemble d'arêtes. J'ai un code pour calculer l'existence et le poids de chaque arête: si cela vous aide, vous pouvez supposer que c'est une fonction 'make_edge (src, dst)', qui retourne une description. Les solutions qui utilisent 'map' ou d'autres fonctions associées pour appeler' make_edge' sont plausibles, et peut-être la bonne chose à faire, mais transformer le corps de la boucle for en une fonction n'est pas moins de répétition que je l'ai déjà fait. résoudre ce problème AFAIK. –

Répondre

5

Vous pouvez utiliser de simples boucles imbriquées pour-:

for src in universe: 
    for dst in universe: 
     if src == dst: 
     continue 
     ... 

Je dirais que c'est le plus facile à lire la syntaxe dans ce cas.

+0

Vous pourriez bien avoir raison. Je déteste quand le code purement impératif est le plus simple ;-) –

+0

Accepter cette réponse, que je lis comme, "non, vous ne pouvez pas * beaucoup * mieux" ... –

1

itertools.product peut prendre un argument mot-clé « répéter » si vous voulez avoir la même séquence que plus d'un paramètre:

itertools.product(universe, repeat=2) 

il est une question d'opinion quant à savoir si cela est plus facile à lire.

Vous pouvez remplacer votre code d'origine par:

for (src, dest) in filter(lambda (a,b): a!=b, itertools.product(universe, repeat=2)): 
    ... 
+0

Nice. Je ne savais pas que tu pouvais faire ça. –

+0

Comme vous utilisez un 'filter' avec un' lambda', cela ne coûte pas plus cher de faire 'for (src, dest) dans ((a, b) pour a, b dans itertools.product (univers, repeat = 2) si a! = b): '. ** NOTE: ** Qui fait la même chose. Oui. Mon point de vue est de laisser OP décider lequel est le plus lisible. – JeromeJ

3

Je suggère maintenant entièrement fonctionnel ou entièrement compréhensions. Voici une implémentation entièrement fonctionnelle.

import itertools 
import operator 

def inner_product(iterable): 
    "the product of an iterable with itself" 
    return itertools.product(iterable, repeat=2) 

def same(pair): 
    "does this pair contain two of the same thing?" 
    return operator.is_(*pair) 

universe = 'abcd' 

pairs = inner_product(universe) 
unique_pairs = itertools.ifilterfalse(same, pairs) 
for pair in unique_pairs: 
    print pair 

""" 
('a', 'b') 
('a', 'c') 
('a', 'd') 
('b', 'a') 
('b', 'c') 
('b', 'd') 
('c', 'a') 
('c', 'b') 
('c', 'd') 
('d', 'a') 
('d', 'b') 
('d', 'c') 
""" 
+0

J'aime ça en principe. En pratique, le code pourrait être maintenu par des personnes qui ne connaissent pas ou à peine Python, donc je ne pense pas que j'ose vraiment l'utiliser cette fois. –