Heim > Artikel > Backend-Entwicklung > Wie erbärmlich ist die for-Schleife in der Go-Sprache?
Dieser Artikel wurde von der Kolumne golang-Tutorial geschrieben, um Ihnen die Interviewfragen zur Go-for-Schleife vorzustellen. Ich weiß nicht, wie viel Sie über die for-Schleife wissen. Glauben Sie, dass es eine Falle ist? Im Folgenden werde ich ausführlich auf For-bezogene Themen eingehen. Ich hoffe, dass es Freunden, die es brauchen, hilfreich sein wird!
Ich weiß nicht, wie viele Go-Interviewfragen und Leaks mit for-Schleifen zusammenhängen. Heute habe ich einen genaueren Blick auf das Wochenende geworfen und die Neudefinition der Schleifenvariablensemantik entdeckt.
Der berühmte Hardcore-Tycoon Russ Cox sagte, er habe sich mit diesem Thema befasst und sagte, dass die Erfahrung von zehn Jahren zeige, dass die Kosten der aktuellen Semantik sehr hoch seien.
Problem
Fall 1: Adresszeichen abrufen
Wenn wir in der Go-Sprache für Anweisungen schreiben, sind die Ergebnisse der Operation und des Ratens manchmal inkonsistent. Zum Beispiel der Code des ersten Falls unten:
var all []*Itemfor _, item := range items { all = append(all, &item) }
Stimmt etwas mit diesem Code nicht? Was wird in der Variablen item in der Variablen all gespeichert? Ist es der Elementwert jeder Schleife?
Tatsächlich wird während der for-Schleife jedes Mal dasselbe Element in der Variablen gespeichert, was dem Elementwert der letzten Schleife entspricht.
Dies ist eine Frage, die häufig in Go-Interviews auftaucht. Sie ist in Kombination mit Goroutine immer noch interessant.
Wenn Sie dieses Problem lösen möchten, müssen Sie das Programm wie folgt umschreiben:
var all []*Itemfor _, item := range items { item := item all = append(all, &item) }
Um eine Elementvariable erneut zu deklarieren, speichern Sie die Elementvariable der for-Schleife und hängen Sie sie dann an.
Fall 2: Abschlussfunktion
Das Folgende ist der Code des zweiten Falls:
var prints []func()for _, v := range []int{1, 2, 3} { prints = append(prints, func() { fmt.Println(v) }) }for _, print := range prints { print() }
Was ist die Ausgabe dieses Programms? Ohne &, um das Adresszeichen zu erhalten, werden 1, 2, 3 ausgegeben?
Das Ausgabeergebnis ist 3, 3, 3. Warum ist das so?
Einer der Kernpunkte des Problems ist die Verschlussfunktion. Tatsächlich drucken alle Verschlüsse das gleiche v. Die Ausgabe ist 3, da nach dem Ende der for-Schleife der Endwert von v auf 3 gesetzt wird, und das war’s.
Wenn Sie den gewünschten Effekt erzielen möchten, müssen Sie weiterhin die universelle Neuzuweisung verwenden. Der neu geschriebene Code lautet wie folgt:
for _, v := range []int{1, 2, 3} { v := v prints = append(prints, func() { fmt.Println(v) }) }
Fügen Sie die Anweisung v := v
hinzu, und die Ergebnisse der Programmausgabe sind 1, 2, 3.
Sehen Sie sich die von Ihnen geschriebenen Go-Projekte sorgfältig an. Sind Ihnen alle bekannt? Mit dieser Transformationsmethode haben wir gewonnen. v := v
语句,程序输出结果为 1,2,3。
仔细翻翻你写过的 Go 工程,是不是都很熟悉?就这改造方法,赢了。
尤其是配合上 Goroutine 的写法,很多同学会更容易在此翻车。
解决方案
修复思路
实际上 Go 核心团队在内部和社区已经讨论过许久,希望重新定义 for 循环的语法。要达到的目的是:使循环变量每次迭代而不是每次循环。
解决的办法是:在每个迭代变量 x 的每个循环体开头,加一个隐式的再赋值,也就是 x := x
Lösung
Machen Sie die Schleife bei jeder Iteration variabel und nicht bei jedem Durchlauf der Schleife
.Die Lösung ist: Fügen Sie am Anfang jedes Schleifenkörpers jeder Iterationsvariablen x eine implizite Neuzuweisung
hinzu, d. h.x := x
, die das obige Programm lösen kann. Es ist dasselbe wie das, was wir jetzt tun, außer dass wir es manuell hinzufügen. Das Go-Team verarbeitet es implizit im Compiler.
Lassen Sie die Benutzer selbst entscheidenWas noch peinlicher ist, ist, dass das Go-Team die Neudefinition der Sprache im Vorschlag: Go 2-Übergang verbietet, sodass rsc dies nicht direkt tun kann. Es liegt also am Benutzer, dieses „Brechen“ zu kontrollieren, indem er die Semantik basierend auf der go-Zeile in der go.mod-Datei jedes Pakets ändert. Wenn wir die in diesem Artikel besprochene for-Schleife in Go1.30 auf Iteration umstellen, dann ist die go-Versionsdeklaration in der Datei go.mod ein Schlüssel. Wie unten gezeigt: 🎜🎜🎜Go 1.30 oder höher iteriert die Variable jedes Mal, während frühere Go-Versionen die Variable jedes Mal in einer Schleife durchlaufen. 🎜🎜Auf diese Weise werden die oben genannten for-Schleifenprobleme bis zu einem gewissen Grad gelöst. 🎜🎜🎜Zusammenfassung🎜🎜🎜Das Problem der Variablen in For-Schleifen war schon immer ein Lieblingsthema großer Go-Prüfer. Darüber hinaus trifft es zu, dass man bei der tatsächlichen Programmierung von Go-Code auf solche Fallstricke stößt. 🎜🎜Obwohl rsc hofft, Pionierarbeit bei go.mod-Dateien zu leisten und Go-Versionsdeklarationen zu verwenden, um die Semantik zu ändern (Hinzufügen und Löschen sind nicht zulässig). Dies öffnet zweifellos eine Hintertür zur Go1-Kompatibilitätsgarantie. 🎜🎜Wenn diese Änderung implementiert wird, führt sie zu einer unterschiedlichen Semantik zwischen der vorherigen und späteren Version von Go. Könnte genauso gut ein semantischer Schalter in einer go.mod-Datei werden. 🎜🎜Das ist offensichtlich eine sehr schwierige Frage, über die man nachdenken muss. 🎜
Das obige ist der detaillierte Inhalt vonWie erbärmlich ist die for-Schleife in der Go-Sprache?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!