post Image
Goのfor文で関数のリストを作ろうとして嵌った話

まずはこれを見てくれ。コイツをどう思う?

func main() {
   xs := []int{1, 2, 3}

   var cmds []func()
   for _, x := range xs {
      cmds = append(cmds, func() { fmt.Println(x) })

      fmt.Println("append: " + strconv.Itoa(x))
      for _, cmd := range cmds {
         fmt.Print("func eval: ")
         cmd()
      }
      fmt.Println()
   }
}

リストxsの値を利用する無名関数xsを作る。まあ、それなりにオーソドックスな処理だと思います。わたしの期待値としては、こうなるはずでした。

$ go run example_lambda.go
append: 1
func eval: 1

append: 2
func eval: 1
func eval: 2

append: 3
func eval: 1
func eval: 2
func eval: 3

しかし、現実はこうでした。

$ go run example_lambda.go
append: 1
func eval: 1

append: 2
func eval: 2
func eval: 2

append: 3
func eval: 3
func eval: 3
func eval: 3

あれ、各関数の実行結果が全部同じに!? そのループ内で触ってないのに値変わってるんだけど… ダイナミックスコープみたいな動きしてるけど、goってクロージャーだからレキシカルスコープだよね???
と混乱していたところ、@tutumingさんより

と、指摘をいただきました。
むむ? ということはXはループ毎に別の変数じゃなくて、ポインタアドレスは全部一緒ってこと?
とりあえず、アドレスを表示してみると…

go run example_lambda.go
append: 1
x addr = %!s(*int=0xc20800a200)
func eval: 1

append: 2
x addr = %!s(*int=0xc20800a200)
func eval: 2
func eval: 2

append: 3
x addr = %!s(*int=0xc20800a200)
func eval: 3
func eval: 3
func eval: 3

おお、確かに一緒だ。でも、ここまでわかれば大丈夫。Goは確か代入時は値で渡すはずだから、こんな感じに変えました。

func main() {
   xs := []int{1, 2, 3}

   var cmds []func()
   for _, x := range xs {
      tmp := x
      cmds = append(cmds, func() { fmt.Println(tmp) })

      fmt.Println("append: " + strconv.Itoa(tmp))
      fmt.Printf("x addr = %s, tmp addr = %s\n", &x, &tmp)
      for _, cmd := range cmds {
      fmt.Print("func eval: ")
        cmd()
      }
      fmt.Println()
   }
}

forループの中で宣言したtmpに一度代入して、それを使うようにします。実行結果は以下の通り。

$ go run example_lambda.go
append: 1
x addr = %!s(*int=0xc20800a200), tmp addr = %!s(*int=0xc20800a208)
func eval: 1

append: 2
x addr = %!s(*int=0xc20800a200), tmp addr = %!s(*int=0xc20800a2b8)
func eval: 1
func eval: 2

append: 3
x addr = %!s(*int=0xc20800a200), tmp addr = %!s(*int=0xc20800a3a0)
func eval: 1
func eval: 2
func eval: 3

期待通り、関数内で参照している値が変わりました。tmp変数のアドレスがx変数とは異なり毎回違うのも確認できます。

しかし、分かってしまえば何ともないかもですが、foreach文でxのアドレスが一緒なのは嵌りそうだな… もっとGo力を鍛えないとですね。

それではHappy Hacking!


『 Go 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。