Als ich auf den Code zurückblickte, den ich vor mehr als zwei Jahren in Python auf einer Programmierseite namens CheckIO geschrieben hatte, war ich ein wenig beeindruckt von dem Vorschlag des Refactorings und teilte ihn als Erklärung mit. Machen.
Die Antwort auf die Frage und der daraus resultierende Thread sind nur für die Person sichtbar, die das Problem gelöst hat Es scheint, dass ich es hier kopieren werde. Alle folgenden Zitate stammen aus dieser Ausgabe und diesem Thread.
Da das Labyrinth in einer zweidimensionalen Anordnung von "0" und "1" passiert wird, ist der Pfad, der zuerst "(10, 10)" von den Koordinaten von "(1, 1)" des Labyrinths erreicht, "N", "S". Rückgabe als Zeichenfolge bestehend aus "E", "W".
"0" steht für den Boden und "1" für die Wand. N
steht für oben, S
steht für unten, E
steht für rechts und W
steht für links. Diese Figur dreht sich alles um. Es kann mehrere Routen geben, aber Sie müssen nur eine Route zurückgeben, die schließlich das Ziel erreicht.
Ich wollte sowieso nicht tippen, also hielt ich die Antwort kurz.
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]
Eine typische rekursive Füllsuche.
Die Verschiebungsfunktion verwendet die bisher zurückgelegten "Pfade", den "Suchursprung" und das "Feld" als Argumente und ruft die Verschiebungsfunktion rekursiv für alle ** Stockwerke ** nach oben, unten, links und rechts auf.
Die Verschiebungsfunktion malt den Pfad, den Sie in eine "Wand" gegangen sind (Feld [x] [y] = 1
), neu, damit Sie den Pfad, von dem Sie gekommen sind, nicht zurückdrehen.
Wenn Sie das Ziel erreicht haben, fügen Sie es als Zielkandidaten zum Ergebnisarray hinzu. Dadurch werden alle Richtungen, die das Ziel erreichen können, in das Array eingefügt. Geben Sie am Ende den Anfang und das Ende zurück.
Normalerweise ist es üblich, mit Dyxtra oder A * ohne Verschwendung zu suchen, aber da ich es trotzdem kurz geschrieben habe, passt es in 12 Zeilen.
Obwohl diese Antwort selbst etwas verwirrend ist, scheinen einige Leute in der Lage zu sein, das Problem zu lösen, das zunächst ärgerlich schien, und ich erhielt die meisten Stimmen unter den Antworten auf diese Antwort.
Teilweise wegen der Aufregung des Threads lieferte veky eine höfliche Überprüfung und Umgestaltung. Das Hauptthema ist von hier.
Da die Antwort auf Englisch ziemlich lang ist, werde ich nur den wichtigen Teil der Antwort erklären.
Die Antwort, die er gegeben hat, ist:
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())
Es wurde auf 9 Zeilen reduziert. Die Punkte, die verbessert wurden, sind wie folgt.
Python kann den Argumentwert angeben, wenn das Argument weggelassen wird, indem =
und die rechte Seite zur formalen Argumentliste der Funktion hinzugefügt werden.
Im Fall von Python können Sie mehrere Begriffe gleichzeitig vergleichen. Die Anweisung "x == 10 und y == 10" kann mechanisch als "x == y == 10" umgeschrieben werden.
Grob gesagt ist die Generator-Anweisung in Python eine Variante der Funktion, die "Yield" anstelle von "Return" verwendet.
Wenn Sie "Yield" in eine Funktion schreiben, wird die Funktion als ** Generatorgenerierungsfunktion ** interpretiert, die das Argument für Yield zurückgibt. Wenn Sie der Funktion einen Wert geben und ihn aufrufen, wird ein ** Generator ** generiert.
Jedes Mal, wenn die Funktion next ()
aufgerufen wird, wird der Generator von der Innenseite der Funktion bis zur nächsten Ausbeute ausgeführt.
Es ist üblich, die Funktion "next ()" aufzurufen und zu stoppen, wenn die Ausnahme "StopIteration" ausgelöst wird. In Python können Sie also einfach alle Elemente lecken, indem Sie der Anweisung "for" einen Generator geben.
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)
:Ausführungsergebnis:
apple: 3
banana: 3
orange: 3
Wenn Sie eine Verarbeitung wiederholen, möchten Sie häufig, dass die Funktion einzelne Zustände hat, aber Sie möchten sie nicht von außen übergeben. Normalerweise ist es ehrlich, eine Klasse zu definieren und als Mitglied zu haben, aber es gibt viele Fälle, in denen dasselbe durch Ausdrücken mit einem Generator erreicht werden kann und es noch übersichtlicher und in Kürze geschrieben werden kann.
In Python gibt es "Liste" und "Tupel", die sich wie Arrays verhalten.
Auf beide kann über Indizes zugegriffen werden, aber die Liste wird durch "[]" und das Tupel durch "()" dargestellt.
list
kann Elemente hinzufügen / entfernen und Werte für tiefgestellte Referenzen mit append
usw. neu schreiben. Dies erwartet der Programmierer als Array.
python:list.py:
a = []
a.append(2) # a == [2]
a.append(3) # a == [2, 3]
a[1] = 10 # a == [10, 3]
tuple
kann nicht wie append
geändert oder in eine tiefgestellte Referenz umgeschrieben werden.
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
Wenn Sie also programmieren, können Sie als klare Botschaft ausdrücken: "Dies ist ein Wert, von dem Sie nicht erwarten, dass er während der Ausführung neu geschrieben wird."
Es ist üblich, den Wert eines Taples mehreren Variablen zuzuweisen. Sie können gleichzeitig zuweisen, indem Sie mehrere Variablen mit Kommas in den Wert der Zuweisungsanweisung schreiben.
python
tup = (1,2,3)
a, b, c = tup
print(a) # 1
print(b) # 2
print(c) # 3
Dies kann auch am Anfang der for-Anweisung verwendet werden.
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))
:Ausführungsergebnis:
x=-1 y=0 way=W
x=1 y=0 way=E
x=0 y=-1 way=N
x=0 y=1 way=S
In meinem ersten Code gab es eine implizite Angabe, dass "von dem Array der Länge 3 die erste die x-Koordinate, die zweite die y-Koordinate und die dritte die Richtung ist", und der Indexzugriff wurde ursprünglich entsprechend durchgeführt. Die Assoziation zwischen nicht trivialen Zahlen und Bedeutungen (allgemein bekannt als ** magische Zahlen **) sollte nicht verwendet werden. Es ist einfacher, die Bedeutung zu vermitteln, wenn Sie jede Variable entpacken und benennen.
Durch die Verwendung von "Ausbeute aus" aus Python 3.3 wird der Generator mit rekursiv prägnant ausgedrückt. Ich denke, dass viele Leute mit "Ausbeute von" nicht vertraut sind. Um es ein wenig richtig zu erklären, ist es ein Satz für die erfolgreiche Übertragung des Generators.
python:yield_from.py:
def foo():
yield 1
yield 2
def gen():
for x in range(5):
yield from foo() #Verwenden Sie die Ausbeute von hier
d = gen()
for x in d:
print(x)
:Ausführungsergebnis:
1
2
1
2
1
2
1
2
1
2
Obwohl es nicht in den Sinn kommt, selbst wenn es als Übertragung bezeichnet wird, wird das gleiche Ergebnis zurückgegeben, selbst wenn die Funktion "gen ()" wie folgt umgeschrieben wird.
python:without_yield_from.py:
def gen():
for x in range(5):
for f in foo():
yield f
Dies ist eine nützliche Notation, wenn Sie möchten, dass stattdessen die Ergebnisse anderer (oder rekursiv selbst) Generatoren zurückgegeben werden.
――Wenn Sie auch nur einen heiklen Code verfügbar machen, erhalten Sie möglicherweise nützliches Wissen. Lassen Sie uns diesen also immer mehr offenlegen. ――CheckIO ist ein guter Ort, um Einblicke in das Forum zu erhalten