ホームページ >バックエンド開発 >Golang >Go 言語の for ループはどれほど悲惨なのでしょうか?

Go 言語の for ループはどれほど悲惨なのでしょうか?

藏色散人
藏色散人転載
2022-11-07 16:48:132614ブラウズ

この記事は、Go for ループに関するインタビューの質問を紹介するために golang チュートリアル コラムによって書かれたものです。for ループについてどれだけ知っているかわかりません。トラップ?以下では、for 関連の問題について詳しく説明します。必要な友人の役に立てば幸いです。

Go の面接での質問やリークが for ループに関連しているものがどれだけあるのかわかりません。今日、週末に詳しく調べたところ、ループ変数のセマンティクスを再定義していることがわかりました。

有名な筋金入りの大物ラス・コックス氏は、この問題を研究しており、10年の経験から現在のセマンティクスのコストが非常に高いことがわかっていると述べました。

質問

  • ケース 1: アドレス文字を取得する

Go 言語では、 write for ステートメントを使用すると、実行結果と推測結果が一致しないことがあります。たとえば、以下の最初のケースのコード:

var all []*Itemfor _, item := range items {
	all = append(all, &item)
}

このコードに何か問題がありますか?変数allのitem変数には何が格納されるのでしょうか?各ループの項目値でしょうか?

実際には、for ループ中は毎回同じ項目が変数に格納されます。これが最後のループの項目値です。

これはGoのインタビューでよく出てくる質問ですが、Goroutineと組み合わせるとさらに面白いのですが、やはり出力の順序が狂うなどの問題がまだあります。

この問題を解決するには、次のようにプログラムを書き直す必要があります。

var all []*Itemfor _, item := range items {
	item := item
	all = append(all, &item)
}

item 変数を再宣言するには、for ループの item 変数を格納してから追加します。 。

  • ケース 2: クロージャ関数

以下は 2 番目のケースのコードです:

var prints []func()for _, v := range []int{1, 2, 3} {
	prints = append(prints, func() { fmt.Println(v) })
}for _, print := range prints {	print()
}

このプログラムとは? の出力& アドレス文字がないと、1、2、3 が出力されますか?

出力結果は 3, 3, 3 です。どうしてこれなの?

この問題の重要なポイントの 1 つはクロージャ関数です。実際、すべてのクロージャは同じ v を出力します。 for ループの終了後、v の最終値は 3 に設定され、それだけであるため、出力は 3 になります。

望ましい効果を達成したい場合は、やはりユニバーサル再割り当てを使用する必要があります。書き換えられたコードは次のとおりです。

for _, v := range []int{1, 2, 3} {
		v := v
		prints = append(prints, func() { fmt.Println(v) })
	}

v := v ステートメントを追加すると、プログラムの出力結果は 1、2、3 になります。 自分が作成した Go プロジェクトを注意深く調べてください。それらはすべてあなたにとって馴染みのあるものですか?この変身方法で勝てました。

特にGoroutineの書き方では、多くの学生がここでひっくり返る可能性が高くなります。

解決策

  • アイデアの修正

実際、Go コア チームは社内および社内でコミュニティ 長い間議論した後、for ループの構文を再定義したいと考えています。達成すべき目標は次のとおりです。 ループのたびに ではなく、反復ごとにループを変数にします。

解決策は次のとおりです。各反復変数 x の各 ループ本体の先頭に暗黙的な再代入 を追加します。つまり、x := x です。これにより、上記のプログラムに隠された落とし穴。これは、手動で追加することを除いて、現在行っていることと同じですが、Go チームが行っていることは、コンパイラーで暗黙的にそれを処理することです。

  • ユーザー自身に決定してもらいましょう

さらに恥ずかしいのは、Go チームが提案: Go 2 移行で言語を再定義することを禁止していることです。 rsc を直接実行することはできません。

したがって、各パッケージの go.mod ファイルの go 行に基づいてセマンティクスを変更することで、この「破壊」を制御するのはユーザーの責任になります。

この記事で説明した for ループを Go1.30 の反復に変更すると、go.mod ファイル内の go バージョン宣言がキーになります。

以下に示すように:

Go 1.30 以降は変数を毎回反復処理しますが、それ以前の Go バージョンは変数を毎回反復処理します。

このようにして、上記の for ループの問題は一定の範囲内で解決されます。

要約

for ループ中の変数の問題は、常に主要な Go 試験官のお気に入りのトピックであり、実際にこの問題が発生することも事実です。 Go コードのプログラミング。一種の落とし穴です。

rsc は go.mod ファイルを開拓し、go バージョン宣言を使用してセマンティクスを変更することを望んでいます (追加と削除は許可されていません)。これは間違いなく、Go1 互換性保証へのバックドアを開きます。

この変更が実装されると、Go の以前のバージョンと新しいバージョンの間でセマンティクスが異なります。 go.mod ファイル内のセマンティック スイッチになる可能性もあります。

これは明らかに非常に難しい質問です。

以上がGo 言語の for ループはどれほど悲惨なのでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.imで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。