je souhaite deplacer des elements dans une liste, et je voudrai savoir quelle est la meilleure methode pour ca. Pour l'instant, j'ai
def moveUpInList(inputList, target=[], iter=1):
target = target if isinstance(target, list) else [target]
for i, e in enumerate(inputList):
for f in target:
if e == f:
if not i==0:
inputList.pop(i)
inputList.insert(i-iter, e)
return inputList
du coup, si j'execute :
l = ['0', '1', '2', '3', '4', '5']
print l
print moveUpInList(l, '2', 1)
il me return bien
['0', '1', '2', '3', '4', '5']
['0', '2', '1', '3', '4', '5']
Mais quand j'essaye de le faire dans la direction opposee, bien sur, ca ne marche plus puisque l'ordre de mes elements est different.
D'une part, je pense que cette methode est assez lourde, et je suis un peu bloque pour la faire fonctionner dans le sens inverse (vers le bas)
...
for i in liste:
if mode == 0:
if compte == (pos_de_objet_dans_liste -1 + decalage % len(liste)):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte])
else:
nouvelle_liste.append(liste[compte])
else:
if compte == (pos_de_objet_dans_liste -1 - decalage % len(liste)):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte])
else:
nouvelle_liste.append(liste[compte])
compte += 1
...
Donc quand le mode == 0, il va decaler a droite. Sinon si le mode == 1, il va decaler a gauche.
Je suis encore débutant, donc je pense qu'on peut clairement réduire ce code, mais bon, c'est un bon début !
Au moins c'est un début. Et en effet, on peut réduire "mécaniquement" un code comme
if compte == (pos_de_objet_dans_liste -1 + decalage % len(liste)):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte])
else:
nouvelle_liste.append(liste[compte])
en remarquant que les deux branches du if se terminent par la même instruction, qu'on peut donc "factoriser" en la sortant du if
if compte == (pos_de_objet_dans_liste -1 + decalage % len(liste)):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte])
(et le else saute, parce qu'il n'y a plus rien dedans)
De même pour le if suivant, qui devient
if compte == (pos_de_objet_dans_liste -1 - decalage % len(liste)):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte])
et du coup du coup, c'est pareil pour le if mode == 0, dont les deux branches se terminent pareil et qui devient, dukou dukou
if mode == 0:
if compte == (pos_de_objet_dans_liste -1 + decalage % len(liste)):
nouvelle_liste.append(objet)
else:
if compte == (pos_de_objet_dans_liste -1 - decalage % len(liste)):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte])
Bref, quand on voit qu'il y a du code qui se répète, au lieu de copier coller, il faut regarder comment le factoriser (code folding).
Ca n'empêche pas bien sur de réfléchir à d'autres améliorations.
Parce que là on voit qu'on utilise deux fois l'expression decalage % len(liste). On pourrait faire
d = decalage % len(liste)
et d apparaitrait dans les deux if, avec un signe différent. Mais on pourrrait changer le signe selon mode (si on y tient)
if mode != 0:
d = -d
et comme ça il n'y aurait plus qu'un seul if
if compte == (pos_de_objet_dans_liste - 1 + d):
nouvelle_liste.append(objet)
nouvelle_liste.append(liste[compte]
Donc
pliage de code
utilisation de variables intermédiaires pour les sous-expressions communes
Après il faut mettre des tests autour pour gérer le premier et le dernier item. Et comme il semble que vous voulez travailler sur des valeurs (et non des index/positions), il faut traiter le cas où cette valeur apparaît plusieurs fois.
Il faudrait aussi spécifier si l'opération doit altérer la liste existante, ou renvoyer une nouvelle liste (en laissant intacte la liste d'origine, de préférence).
Bon, ce qui serait bien, c'est de commencer par faire un peu d'analyse du problème. On suppose qu'on a la liste ['a', 'b', 'c', 'd', 'e'] et qu'on va pas demander des trucs impossibles.
décaler un truc vers la droite, on fait comment ? Disons décaler 'b' de n=2 positions vers la droite. On est d'accord qu'on se retrouvera avec ['a', 'c', 'd', 'b', 'e'] ? Sinon c'est qu'on parle pas de la même chose... Bref, 'b' qui était à l'indice 1 se retrouve à l'indice 3. Il ne vous échappe sans doute pas que 3 = 2 + 1, et on peut même se convaincre qu'en général l'indice final, c'est l'indice initial + n (le décalage voulu vers la droite)
vers la gauche ? on repart de la même liste, on décale 'd' (qui est à l'indice 3) de n = 2 vers la gauche, on trouve ['a', 'd', 'b', 'c', 'e'], le 'd' est à à l'indice 1 = indice initial - n
Si je ne me suis pas trompé, ça va pas être compliqué de faire une fonction qui travaille directement dans la liste (en la modifiant)
trois paramètres : la liste à modifier, la valeur à déplacer, et la décalage voulu (négatif vers la gauche)
un coup d'index pour trouver l'indice initial
un coup de remove pour retirer la valeur
et un insert pour le remettre au bon endroit (indice initial + décalage).
--- EDIT
Bon, mise en œuvre, c'est tout bête
def decaler_dans_liste(liste, valeur, decalage):
"""modifie la liste en déplaçant la première instance
de valeur de decalage positions vers la droite.
(décalage vers la gauche si négatif"""
indice = liste.index(valeur)
liste.pop(indice)
liste.insert(indice + decalage, valeur)
on choisit pop parce que liste.remove(valeur), ça ferait re-parcourir la liste, alors qu'on a déjà l'indice de la valeur à déplacer.
Bon, c'est pas tout de pondre du code magique, il faut aussi montrer que ça passe bien quelques tests. J'utilise un bête assert, les frameworks de tests, c'est pour quand on s'est totalement convaincu qu'on allait en écrire systématiquement, ce qui ne correspond pas à la réalité des débutants (hélas). Assert, c'est déjà ça...
La fonction vérifier_decalage, elle effectue un décalage défini par les 3 premiers paramètres, et elle regarde si le résultat est celui attendu (dernier paramètre) avec un assert.
Important : comme la fonction modifier_decalage a pour mission de modifier la liste qui lui est transmise, vérifier_décalage travaille sur une copie pour ne pas niq*er le contenu des variables ab, ba, etc. qui servent plusieurs fois dans les tests
Le Tout est souvent plus grand que la somme de ses parties.