J'ai été surpris de recevoir une belle critique lorsque j'ai écrit Python à CheckIO et son explication

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.

problème

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.

problem.png

Ma (pas bonne) réponse

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]

Commentaire

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.

Avis reçu

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.

voted.png

En partie à cause de l'excitation du fil, veky a fourni une critique et une refactorisation polies. Le sujet principal est d'ici.

veky.png

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.

Utiliser les arguments par défaut

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.

Utilisation de la comparaison à plusieurs termes

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».

Utilisation du générateur

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.

Utilisation de tapple

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."

Déballage Taple

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.

Utilisation du rendement de la déclaration

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.

Résumé

――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

J'ai été surpris de recevoir une belle critique lorsque j'ai écrit Python à CheckIO et son explication
Une histoire sur l'écriture d'AWS Lambda et de devenir un peu accro aux valeurs par défaut des arguments Python
J'ai écrit une classe en Python3 et Java
Une note à laquelle j'étais accro lors de l'exécution de Python avec Visual Studio Code
Une histoire à laquelle j'étais accro après la communication SFTP avec python
Ce à quoi j'étais accro lors de l'utilisation de Python tornado
J'ai écrit rapidement un programme pour étudier la DI avec Python ①
Ce à quoi j'étais accro lorsque l'utilisateur de traitement est passé à Python
Je veux ajouter un joli complément à input () en python
Essayez simplement de recevoir un webhook avec ngrok et Python
Quand j'ai essayé de gratter en utilisant des requêtes en python, j'étais accro à SSLError, donc un mémo de contournement
J'ai été surpris de savoir comment enregistrer des objets avec python, qui est léger et très économe en énergie.
Ce à quoi j'étais accro en présentant ALE à Vim pour Python
Une note à laquelle j'étais accro lors de l'émission d'un bip sous Linux
J'ai écrit une fonction pour charger le script d'extension Git en Python
J'ai essayé de faire un processus d'exécution périodique avec Selenium et Python
Une note à laquelle j'étais accro lors de la création d'une table avec SQL Alchemy
J'ai écrit un script pour extraire les liens de pages Web en Python
J'étais accro aux variables de classe et aux variables d'instance erronées en Python
[Python] J'ai écrit une API REST en utilisant AWS API Gateway et Lambda.
J'ai écrit un script pour aider goodnotes5 et Anki à travailler ensemble
Je suis mort en pensant qu'une variable de classe Python était une variable d'instance
[Python] Quand j'ai essayé de créer un outil de décompression avec un fichier zip que je connaissais juste, j'étais accro à sys.exit ()
J'ai été surpris quand j'ai envoyé des autorisations de visage depuis iPad avec udp et que je les ai relayées, reçues et affichées avec Python!
J'ai écrit un code pour convertir quaternion en angle de graissage de type z-y-x avec Python
Lorsque j'ai essayé d'exécuter Python, j'ai été ignoré dans le Microsoft Store
J'ai eu une erreur en essayant d'installer Xgboost et sa solution
J'étais accro à la création d'un environnement Python venv avec VS Code
Quand je screenfetch avec xonsh, python x.x sort et c'est triste
J'obtiens un UnicodeDecodeError en essayant de me connecter à oracle avec python sqlalchemy
Le nom du fichier était mauvais en Python et j'étais accro à l'importation
J'ai créé un exemple pour accéder à Salesforce en utilisant Python et Bottle
Je souhaite créer une application Web en utilisant React et Python flask
Je veux créer un environnement Python
Ce que j'étais accro à Python autorun
J'ai essayé de faire un processus périodique avec CentOS7, Selenium, Python et Chrome
Ce à quoi j'étais accro lors de la création d'applications Web dans un environnement Windows
Je veux juste ajouter scipy, mais c'est un mémo amusant. Ubuntu, Python 3.
Je veux déposer un fichier sur tkinter et obtenir son chemin [Tkinter DnD2]
Quand j'ai essayé de créer un environnement virtuel avec Python, cela n'a pas fonctionné
Lorsque je me suis connecté à un serveur Jupyter distant avec VScode, il était distant mais local.
Je veux exécuter et distribuer un programme qui redimensionne les images Python3 + pyinstaller
Notez que j'étais accro à la configuration de TensowFlow
Une histoire dont j'ai été très convaincu lorsque j'ai écrit le code du problème Monty Hall et calculé le taux de gain
Cela fait un an que j'ai rejoint le département de développement d'applications Web, alors je regarde en arrière
Un mémo que j'ai écrit un tri rapide en Python
Python: j'ai pu récurer en lambda
Un joli nimporter qui connecte nim et python
Je veux créer une fenêtre avec Python
Je veux faire un jeu avec Python
J'ai écrit "Introduction à la vérification des effets" en Python
Remarque Python: lors de l'attribution d'une valeur à une chaîne
J'ai écrit un script pour télécharger un plug-in WordPress
python memo- "sinon A et B" était "si (pas A) et B"
Ce que j'ai fait lors de la mise à jour de Python 2.6 vers 2.7
Je veux écrire dans un fichier avec Python
J'étais sobrement accro à appeler awscli à partir d'un script Python 2.7 enregistré dans crontab
Une histoire qui était pratique lorsque j'ai essayé d'utiliser le module d'adresse IP python
[Python] Je souhaite utiliser uniquement l'index lors de la mise en boucle d'une liste avec une instruction for