Itérer sur une liste privée de certains éléments.

Le
Kaoron
Bonjour,

Pouvez-vous m'enrichir d'une méthode élégante pour itérer sur une liste
privée de certains de ses éléments ?

La solution que j'emploie actuellement est celle-ci :
>for e in list1 :
> if not e in list2 :
> <instructions>

Elle a le désavantage de diviser la définition de mon ensemble itéré sur
deux instructions, et d'approfondir inutilement l'indentation.


J'avais envisagé les listes en intention, ce qui me donnait ceci :
>for e in [e for e in list1 if e not in list2]:
> <instructions>

Mais comme vous pouvez l'apprécier, ça n'a plus grand chose de lisible.


La syntaxe suivante dérivée des listes en intention ne fonctionne pas :
>for e in list1 if e not in list2 :

Avez-vous une meilleure solution à me proposer ?
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Alain BARTHE
Le #20499201
Kaoron a écrit :
Bonjour,

Pouvez-vous m'enrichir d'une méthode élégante pour itérer sur une liste
privée de certains de ses éléments ?

La solution que j'emploie actuellement est celle-ci :
>for e in list1 :
> if not e in list2 :
> <instructions>

Elle a le désavantage de diviser la définition de mon ensemble itéré sur
deux instructions, et d'approfondir inutilement l'indentation.


J'avais envisagé les listes en intention, ce qui me donnait ceci :
>for e in [e for e in list1 if e not in list2]:
> <instructions>

Mais comme vous pouvez l'apprécier, ça n'a plus grand chose de lisible.


La syntaxe suivante dérivée des listes en intention ne fonctionne pas :
>for e in list1 if e not in list2 :

Avez-vous une meilleure solution à me proposer ?



Tu peux essayer :

for e in filter (<fonction>, list1):
<instruction>

Avec <fonction> qui peut être une fonction booléenne qui fasse ton
filtrage (si cette fonction existe déjà) ou une lambda expression si tu
ne veux pas surcharger ton espace de noms.

Ex:

for e in filter (lambda x: x not in list2, list1):
<instruction>

Le gain n'est quand même pas énorme par rapport à la syntaxe initiale.
Alain BARTHE
Le #20499241
Kaoron a écrit :
Bonjour,

Pouvez-vous m'enrichir d'une méthode élégante pour itérer sur une liste
privée de certains de ses éléments ?

La solution que j'emploie actuellement est celle-ci :
>for e in list1 :
> if not e in list2 :
> <instructions>

Elle a le désavantage de diviser la définition de mon ensemble itéré sur
deux instructions, et d'approfondir inutilement l'indentation.



Ca va surement en faire hurler plus d'un mais j'aime bien
(et puis c'est vendredi) :

for e in list1:
if e in list2: continue
<instructions-sans-approfondir-inutilement-l-indentation>

J'adore faire des trucs comme ça dans les boucles :
on peut mettre plein de tests en début de boucle, pour ignorer toute une
série de cas, sans approfondir inutilement...

Ca évite parfois de se retrouver avec le bloc d'instructions "utiles"
perdu dans une 3eme, 4eme niveau d'indentation, ce qui n'est pas
forcément plus lisible que quelques instructions continue bien
identifiées en début de boucle.


J'avais envisagé les listes en intention, ce qui me donnait ceci :
>for e in [e for e in list1 if e not in list2]:
> <instructions>

Mais comme vous pouvez l'apprécier, ça n'a plus grand chose de lisible.


La syntaxe suivante dérivée des listes en intention ne fonctionne pas :
>for e in list1 if e not in list2 :

Avez-vous une meilleure solution à me proposer ?


Pierre Maurette
Le #20499361
Kaoron, le 06/11/2009 a écrit :
Bonjour,

Pouvez-vous m'enrichir d'une méthode élégante pour itérer sur une liste
privée de certains de ses éléments ?

La solution que j'emploie actuellement est celle-ci :
>for e in list1 :
> if not e in list2 :
> <instructions>

Elle a le désavantage de diviser la définition de mon ensemble itéré sur deux
instructions, et d'approfondir inutilement l'indentation.



Mais elle a l'avantage d'être maintenable sans risque de casser
vicieusement un truc qui marche. Je suis revenu de mon époque "tout
doiot tenir sur une seule ligne" qui me générait des trucs qui me
prenaient dix minutes à détricoter pour modifier par exemple un test.
Les deux trucs proposés par Alain BARTHE sont parfaits sur le plan de
la maintenabilité.

J'avais envisagé les listes en intention, ce qui me donnait ceci :
>for e in [e for e in list1 if e not in list2]:
> <instructions>

Mais comme vous pouvez l'apprécier, ça n'a plus grand chose de lisible.



Si vous trouvez ça illisible, le problème est peut-être chez vous ;-)
Vous pouvez compacter, et pour moi ça reste très lisible:

[instructions(_) for _ in list1 if _ not in list2]
ou
[instructions(_) for _ in filter(<fonction>, list1)]

Selon ce que fait <instructions> vous codez une fonction ou utilisez un
lambda. Vous pouvez aussi regarder du coté de map et reduce.

Je me répète, j'ai perdu pas mal de temps à chercher à coder élégant.
Résultat, je perdais du temps, ce n'était *pas* élégant parce
qu'illisible, bref, j'ai changé mon fusil d'épaule et je vais au plus
rapide et naturel. A vous de voir...

La syntaxe suivante dérivée des listes en intention ne fonctionne pas :
>for e in list1 if e not in list2 :

Avez-vous une meilleure solution à me proposer ?



--
Pierre Maurette
PIGUET Bruno
Le #20500741
Le Fri, 06 Nov 2009 10:55:38 +0100, Kaoron a écrit :

Bonjour,

Pouvez-vous m'enrichir d'une méthode élégante pour itérer sur une liste
privée de certains de ses éléments ?

La solution que j'emploie actuellement est celle-ci :
>for e in list1 :
> if not e in list2 :
> <instructions>




Est-ce une situation où il est possible d'utiliser les ensembles
("sets") ?

Dans un cas simple, j'ai remplacé :
l5 = range(5, 1000, 5)
l3 = range(3, 1000, 3)
l = l3
for i in l5:
if not i in l3:
l.append(i)

par :
s5 = set(range(5, 1000, 5))
s3 = set(range(3, 1000, 3))
s = s3 | s5

avec un bon gain d'efficacité.

Bruno.
Pierre Maurette
Le #20500941
PIGUET Bruno, le 06/11/2009 a écrit :
Le Fri, 06 Nov 2009 10:55:38 +0100, Kaoron a écrit :

Bonjour,

Pouvez-vous m'enrichir d'une méthode élégante pour itérer sur une liste
privée de certains de ses éléments ?

La solution que j'emploie actuellement est celle-ci :
>for e in list1 :
> if not e in list2 :
> <instructions>




Est-ce une situation où il est possible d'utiliser les ensembles
("sets") ?

Dans un cas simple, j'ai remplacé :
l5 = range(5, 1000, 5)
l3 = range(3, 1000, 3)
l = l3
for i in l5:
if not i in l3:
l.append(i)

par :
s5 = set(range(5, 1000, 5))
s3 = set(range(3, 1000, 3))
s = s3 | s5



Je pense que l'opérateur, c'est - et non | (union).

on peut penser à

for e in set(list1) - set(list2):
etc.

Mais avec les set on change d'algo. Même sans parler de l'ordre. Dans
la question on éliminait toutes les occurences d'un élément s'il était
présent dans list2, mais on traitait autant de fois les autres qu'ils
étaient dans list1. Avec les set, on élimine les doublons (et plus).

> avec un bon gain d'efficacité.

Sûr ?

--
Pierre Maurette
Alain Ketterlin
Le #20501451
Pierre Maurette
PIGUET Bruno, le 06/11/2009 a écrit :



Est-ce une situation où il est possible d'utiliser les ensembles
("sets") ?





[Je zappe les remarques pertinentes de Pierre, en particulier sur
l'ordre et les doublons.]

avec un bon gain d'efficacité.



Sûr ?



Sûr. Au moins un facteur N/log(N) probablement, où N est la taill e du
deuxième ensemble. Sauf cas très particulier.

-- Alain.
PIGUET Bruno
Le #20502851
Le Fri, 06 Nov 2009 14:54:50 +0100, Pierre Maurette a écrit :


Mais avec les set on change d'algo.



C'est pour ça que j'ai commencé mon message par :
"Est-ce une situation où il est possible d'utiliser les ensembles
("sets") ?"

Le premier bout de code que j'ai recopié était une façon de faire une
liste unique en disant "si un nouvel élément n'est pas (déjà) dans la
liste, je l'ajoute (append)". Donc l'opérateur de set correspondant était
bien "|".
Mais cette transformation d'exclusion en union n'est pas forcément
celui que souhaitais l'OP. D'où mon introduction prudente.

> avec un bon gain d'efficacité.

Sûr ?



Le bon benchmarking est un art difficile. Sur mon cas, et ce que j'ai pu
en mesurer, oui.

Bruno.
Pierre Maurette
Le #20504151
PIGUET Bruno, le 06/11/2009 a écrit :
Le Fri, 06 Nov 2009 14:54:50 +0100, Pierre Maurette a écrit :


Mais avec les set on change d'algo.



C'est pour ça que j'ai commencé mon message par :
"Est-ce une situation où il est possible d'utiliser les ensembles
("sets") ?"

Le premier bout de code que j'ai recopié était une façon de faire une
liste unique en disant "si un nouvel élément n'est pas (déjà) dans la
liste, je l'ajoute (append)". Donc l'opérateur de set correspondant était
bien "|".
Mais cette transformation d'exclusion en union n'est pas forcément
celui que souhaitais l'OP. D'où mon introduction prudente.



Désolé. Je ne pensais pas que vous êtiez aussi éloigné que ça du
problème de l'OP, et comme souvent j'ai lu en diagonale et ce que je
*voulais* lire, à savoir:

l5 = range(5, 1000, 5)
l3 = range(3, 1000, 3)
l = [] #et non l = l3 !!!
for i in l5:
if not i in l3:
l.append(i)

ce qui fait ce que veut l'OP, aux doublons près bien entendu.

Ce que vous faisiez, c'est la liste des multiples - non nuls - de 3 et
de 5, en faisant attention à ne pas inclure deux fois les multiples
communs, donc les multiples de 15.
C'est effectivement complètement un problème d'ensemble. Le gain de
performance reste énorme même en faisant
s = sorted(list(s3 | s5))

Remarque: par rapport à la solution bourrin, on est - dans certaines
conditions de test - ~10 fois plus rapide en faisant:

l5 = range(5, 1000, 5)
l3 = range(3, 1000, 3)
l15 = range(15, 1000, 15)
l = l3
for i in l5:
if not i in l15:
l.append(i)

mais ça reste au moins 5 fois plus lent qu'avec les ensembles, même en
convertissant le résultat en liste triée.

avec un bon gain d'efficacité.



Sûr ?



Le bon benchmarking est un art difficile. Sur mon cas, et ce que j'ai pu
en mesurer, oui.



J'ai mesuré également, quand le problème est adapté aux ensembles, la
différence est monstrueuse, effectivement. Il est d'ailleurs clair que
set(range()) est traité spécifiquement, et non pas comme
set(listequelconque()).

--
Pierre Maurette
Kaoron
Le #20504911
Alain BARTHE wrote:
Ca va surement en faire hurler plus d'un mais j'aime bien
(et puis c'est vendredi) :

for e in list1:
if e in list2: continue
<instructions-sans-approfondir-inutilement-l-indentation>

J'adore faire des trucs comme ça dans les boucles :
on peut mettre plein de tests en début de boucle, pour ignorer toute une
série de cas, sans approfondir inutilement...

Ca évite parfois de se retrouver avec le bloc d'instructions "utiles"
perdu dans une 3eme, 4eme niveau d'indentation, ce qui n'est pas
forcément plus lisible que quelques instructions continue bien
identifiées en début de boucle.




Les deux solutions que vous proposez me paraissent très lisibles et
explicites, ce qui est en quelque sorte mon critère suprême d'élégance.

Je vois par ailleurs que les deux ont été proposées en réponse à une
proposition de changement de la syntaxe de for sur la ML python-dev :

http://mail.python.org/pipermail/python-dev/2008-May/079556.html
http://mail.python.org/pipermail/python-dev/2008-May/079567.html

C'est le genre d'enrichissement que j'étais venu chercher, merci beaucoup :)
Alain BARTHE
Le #20506491
Kaoron a écrit :
Alain BARTHE wrote:
Ca va surement en faire hurler plus d'un mais j'aime bien
(et puis c'est vendredi) :

for e in list1:
if e in list2: continue
<instructions-sans-approfondir-inutilement-l-indentation>

J'adore faire des trucs comme ça dans les boucles :
on peut mettre plein de tests en début de boucle, pour ignorer toute une
série de cas, sans approfondir inutilement...

Ca évite parfois de se retrouver avec le bloc d'instructions "utiles"
perdu dans une 3eme, 4eme niveau d'indentation, ce qui n'est pas
forcément plus lisible que quelques instructions continue bien
identifiées en début de boucle.




Les deux solutions que vous proposez me paraissent très lisibles et
explicites, ce qui est en quelque sorte mon critère suprême d'élégance.

Je vois par ailleurs que les deux ont été proposées en réponse à une
proposition de changement de la syntaxe de for sur la ML python-dev :

http://mail.python.org/pipermail/python-dev/2008-May/079556.html
http://mail.python.org/pipermail/python-dev/2008-May/079567.html

C'est le genre d'enrichissement que j'étais venu chercher, merci
beaucoup :)



De rien :)
Publicité
Poster une réponse
Anonyme