0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Go】forループのrangeで要素を更新したいとき、2つ目の変数は使えない

Last updated at Posted at 2022-04-10

概要

range が返す変数の仕様で躓いたので、備忘録として残しておきます。
(下記でいうk, v)

package main

import "fmt"

func main() {
	foo := map[string]string{"key": "value"}
	for k, v := range foo {
		fmt.Printf("foo[%v]: %v", k, v)
	}
}

// => foo[key]: value

詳細

結論

range を使うときは以下の方針で書くのがいいと思います。

  • 要素を値を更新したい ⇒ 1つ目の変数を使う
  • 要素を更新したくない ⇒ 2つ目の変数を使う

理由は、

  • 1つ目の変数は配列, mapのindex, keyであり、この値を使えばループしている配列, mapの要素の実体を指定できる
  • 2つ目の変数は1つ目の変数で返されたindex, keyで示される値のコピーである(値渡しである)

というGoの仕様があるからです。つまり、2つ目の引数に再代入しても何も起こらないということですね。
Tour of Goにもしっかり書いてあるのですが、私はハマってみないと理解ができなったようです…。
https://go-tour-jp.appspot.com/moretypes/16

検証

Atcoderのabc081_b - Shift onlyという問題です。

  • n, A1, A2, ...An の標準入力を受け取る
  • A1, A2, ...An が全て偶数か判定する
  • 全て偶数ならA1, A2, ...An の全てを2で割る
  • 再度偶数判定を行う…
  • “全てを2で割る”を実行した回数を出力する

という流れ。

NGのコードはこちら:

package main

import (
	"fmt"
)

func main() {
	var length int
	fmt.Scanf("%d", &length)

	nums := make([]int, length)
	for i, _ := range nums {
		fmt.Scanf("%d", &nums[i])
	}

	var count int
	var flag bool
	for {
		flag = false

		for _, n := range nums {
			if n%2 == 0 {
				flag = true
			} else {
				flag = false
				break
			}
		}

		if !flag {
			break
		}

		count++
		for _, n := range nums {
			n = n / 2
		}
	}

	fmt.Print(count)
}

このコードで下記の入力を与えると処理が終わらず失敗します。

6
382253568 723152896 37802240 379425024 404894720 471526144

原因はこの部分です。

このnnums[index] のコピーなので、ここでn の値を更新しようがnums の中身(つまりA1, A2, ...An)は更新されず、無限ループになるわけです。

		count++
		for _, n := range nums {
			n = n / 2
		}

以下のように、range の1つ目の引数でnums の要素を指定するように修正すればOKのコードになります。

		count++
		for i, _ := range nums {
			nums[i] = nums[i] / 2
		}

先ほどと同じ入力を与えると、正常に処理は完了し、期待値通りの8 が出力されます。

最後に

「問題は配列のループを使えばいいのだからrange を使わなければよかったのでは?」とも思いましたが、”要素の数だけ処理を行う”というのを強調するならrange を使った方がわかりやすいだろうなぁと考えました。

以上です!

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?