Quand j'ai regardé le code que j'ai écrit en Python il y a plus de deux ans sur un site de programmation appelé CheckIO, j'ai été un peu impressionné par la suggestion de refactoring, donc je l'ai partagé comme explication. Faire.
La réponse à la question et le [fil] qui en découle (https://py.checkio.org/mission/open-labyrinth/publications/kumagi/python-3/first/) ne sont visibles que par la personne qui a résolu le problème Il semble que je vais le copier ici. Toutes les citations suivantes proviennent de ce numéro et de ce fil.
Puisque le labyrinthe est passé dans un tableau bidimensionnel de "0" et "1", le chemin qui atteint "(10, 10)" pour la première fois à partir des coordonnées de "(1, 1)" du labyrinthe est "N", "S". Renvoie sous forme de chaîne composée de «E», «W». «0» représente le sol et «1» représente le mur. «N» signifie haut, «S» signifie bas, «E» signifie droite et «W» signifie gauche. Ce chiffre est tout au sujet. Il peut y avoir plusieurs itinéraires, mais vous n'avez qu'à retourner un seul itinéraire qui atteindra éventuellement l'objectif.
Je ne voulais pas taper de toute façon, donc j'ai gardé la réponse courte.
python:answer.py:
def checkio(data):
result = []
dirs = [[-1, 0, "W"], [+1, 0, "E"], [0, -1, "N"], [0, +1, "S"]]
def move(path, x, y, field):
field[y][x] = 1
if x == 10 and y == 10:
result.append(path)
for d in dirs:
if field[y + d[1]][x + d[0]] == 0:
move(path + d[2], x + d[0], y + d[1], field)
move("", 1, 1, data)
return result[0]
Une recherche de remplissage récursive typique.
La fonction de déplacement prend les «chemins parcourus jusqu'à présent», «l'origine de la recherche» et le «champ» comme arguments, et appelle récursivement la fonction de déplacement à tous les ** étages ** vers le haut, le bas, la gauche et la droite.
La fonction de déplacement repeint le chemin que vous avez parcouru dans un "mur" (field [x] [y] = 1
) afin que vous ne retourniez pas le chemin d'où vous veniez.
Lorsque vous atteignez l'objectif, ajoutez-le au tableau de résultats en tant que candidat d'objectif. Cela mettra toutes les directions qui peuvent atteindre l'objectif dans le tableau, donc à la fin, retournez le début et la fin.
Normalement, il est habituel de rechercher sans gaspillage en utilisant Dyxtra ou A *, mais comme je l'ai écrit court de toute façon, il tient en 12 lignes.
Bien que cette réponse en elle-même soit un peu déroutante, il semble que certaines personnes aient pu résoudre le problème qui semblait ennuyeux au début, et j'ai reçu le plus de votes parmi les réponses à cette réponse.
En partie à cause de l'excitation du fil, veky a fourni une critique et une refactorisation polies. Le sujet principal est d'ici.
La réponse étant assez longue en anglais, je n'expliquerai que la partie importante de la réponse.
La réponse qu'il a fournie est:
python:answer.py:
def checkio(data):
def move(path="", x=1, y=1):
data[y][x] = 1
if x == y == 10:
yield path
for x, y, way in (x-1,y,"W"), (x+1,y,"E"), (x,y-1,"N"), (x,y+1,"S"):
if not data[y][x]:
yield from move(path + way, x, y)
return next(move())
Il a été réduit à 9 lignes. Les points qui ont été améliorés sont les suivants.
Python peut spécifier la valeur de l'argument lorsque l'argument est omis en ajoutant =
et le côté droit à la liste d'arguments formels de la fonction.
Dans le cas de Python, vous pouvez comparer plusieurs termes à la fois. L'instruction «x == 10 et y == 10» peut être réécrite mécaniquement comme «x == y == 10».
En gros, l'instruction du générateur en Python est une variante de la fonction qui utilise «yield» au lieu de «return».
Si vous écrivez yield
dans une fonction, la fonction est interprétée comme une ** fonction de génération de générateur ** qui renvoie l'argument donné à yield. Si vous donnez une valeur à la fonction et que vous l'appelez, un ** générateur ** sera généré.
Chaque fois que la fonction next ()
est appelée, le générateur est exécuté de l'intérieur de la fonction jusqu'au rendement suivant.
Il est courant d'appeler la fonction next ()
et de s'arrêter lorsque l'exception StopIteration
est lancée, donc en Python vous pouvez facilement lécher tous les éléments en donnant un générateur à l'instruction for
.
python:generator.py:
def fruit_generator(num):
yield "apple: " + str(num)
yield "banana: " + str(num)
yield "orange: " + str(num)
for fruit in fruit_generator(3):
print(fruit)
:Résultat d'exécution:
apple: 3
banana: 3
orange: 3
Souvent, lorsque vous répétez un traitement, vous voulez que la fonction ait des états individuels, mais vous ne voulez pas la transmettre de l'extérieur. En règle générale, il est honnête de définir une classe et de l'avoir en tant que membre, mais il existe de nombreux cas où la même chose peut être faite en l'exprimant avec un générateur et elle peut être écrite encore plus proprement et rapidement.
En Python, il y a «list» et «tuple» qui se comportent comme des tableaux.
Les deux sont accessibles par des indices, mais la liste est représentée par «[]» et le tuple est représenté par «()».
list
peut ajouter / supprimer des éléments et réécrire les valeurs des références en indice en utilisant ʻappend` etc. C'est ce que le programmeur attend en tant que tableau.
python:list.py:
a = []
a.append(2) # a == [2]
a.append(3) # a == [2, 3]
a[1] = 10 # a == [10, 3]
tuple
ne peut pas être modifié tel que ʻappend` ou réécrit en une référence en indice.
python:tuple.py:
a = (2,3,4)
a[2] # 4
a.append(5) # AttributeError: 'tuple' object has no attribute 'append'
a[1] = 3 #TypeError: 'tuple' object does not support item assignment
Ainsi, lors de la programmation, vous pouvez exprimer sous la forme d'un message clair: "C'est une valeur que vous ne vous attendez pas à réécrire lors de l'exécution."
Il est courant de réaffecter la valeur d'un taple à plusieurs variables. Vous pouvez affecter à la fois en écrivant plusieurs variables avec des virgules dans la lvalue de l'instruction d'affectation.
python
tup = (1,2,3)
a, b, c = tup
print(a) # 1
print(b) # 2
print(c) # 3
Cela peut également être utilisé au début de l'instruction for.
python
x = 0
y = 0
for x, y, way in (x-1,y,"W"), (x+1,y,"E"), (x,y-1,"N"), (x,y+1,"S"):
print("x={x} y={y} way={w}".format(x=x, y=y, w=way))
:Résultat d'exécution:
x=-1 y=0 way=W
x=1 y=0 way=E
x=0 y=-1 way=N
x=0 y=1 way=S
Dans mon premier code, il y avait une spécification implicite que "du tableau de longueur 3, le premier est la coordonnée x, le second est la coordonnée y et le troisième est la direction", et l'accès en indice a été effectué à l'origine en fonction de cela. Vous ne devez pas utiliser de nombres et d'associations de signification non triviaux (communément appelés ** nombres magiques **). Il est plus facile de transmettre la signification si vous décompressez et nommez chaque variable.
En utilisant yield from
ajouté à partir de Python 3.3, le générateur avec récursif est exprimé de manière concise.
Je pense que beaucoup de gens ne sont pas familiers avec yield from
, donc pour l'expliquer un peu correctement, c'est une phrase pour réussir à transférer le générateur.
python:yield_from.py:
def foo():
yield 1
yield 2
def gen():
for x in range(5):
yield from foo() #utiliser le rendement d'ici
d = gen()
for x in d:
print(x)
:Résultat d'exécution:
1
2
1
2
1
2
1
2
1
2
Bien que cela ne vienne pas à l'esprit même si cela s'appelle transfer, le même résultat est renvoyé même si la fonction gen ()
est réécrite comme suit.
python:without_yield_from.py:
def gen():
for x in range(5):
for f in foo():
yield f
C'est une notation utile lorsque vous souhaitez que les résultats d'autres générateurs (ou de manière récursive) soient renvoyés à la place.
――Si vous exposez même un code délicat, vous pouvez obtenir des connaissances utiles, alors exposons-le de plus en plus. ――CheckIO est un bon endroit pour obtenir des informations sur le forum
Recommended Posts