焼売飯店

GoとかTS、JSとかRubyとか

Go Quiz Advent Calendar【10日目】 - iotaのカラクリ編

こちらは Goクイズ Advent Calendar 2020 - Qiita の10日目の記事です。


問題

今回は、皆さん大好きなiotaの問題です。

package main

import "fmt"

const (
    X       = 0
    A, B, C = iota, iota + 1, iota * 2
    D, _, E
    _, F, _
    G = iota + iota
)

func main() {
    fmt.Println(D + E + F + G)
}

さて、答えはどれでしょう?

  1. compile error
  2. 12
  3. 18
  4. 24

解答

https://play.golang.org/p/6c_X0VSSul4

正解

スクロールした先にあります!

正解は、3の 18 です。

解説

iotaについて考える時、気にしないといけない事はたった一つです。

それは、 iotaがConstDecl中の何番目のConstSpecに現れているか です。

用語を解説します。

ConstDecl, ConstSpecとは

ConstDeclは、次のような形式で行われる 定数宣言 です。

const (
    A = 1
    B = 2
    C
)

const D = 3
const E = 4
const F = 5

A~Cは、const キーワードに始まり、 () で括られている、1つのConstDeclです。 D~Fは、 const キーワードの書かれているそれぞれの行がConstDeclに該当します。

ConstSpecは、上記のコードで示される A = 1 D = 3 など、定数の識別子とその値を指定している部分に該当します。

C もConstSpecに該当します。1つのConstDecl中で右辺を省略したConstSpecを書いた場合、直前のConstSpecの右辺をもう一度書いた事と同じと見なされます (この場合は = 2 を書いているのと同じなので、Cの値は 2 となります)。*1

iotaの正体について

今回のポイントは、 () 付きのConstDeclは、複数のConstSpecを持つことができ、そのindexを内部的に保持している ことです。

そして、iotaの値は ConstDecl中でiotaが使われているConstSpecのindexの値 でしかありません。

これがiotaの正体です。

例を見てみましょう。

const (
    A = iota // index: 0
    B        // index: 1
    C        // index: 2
)

ConstSpecのindexは0 originなので、この場合は A, B, C0, 1, 2 となります。

const (
    A = iota // index: 0
    B = 0    // index: 1
    C = iota // index: 2
)

この例では、Bでiotaを使っていませんが、気にすべきなのはConstSpecのindexのみなので C2 となります。

const (
    A, B = iota, iota // index: 0
    C, D              // index: 1
)

A, B のConstSpec中で2回iotaが登場していますが、ConstSpecのindexは 0 なので、 A, B はいずれも 0 となります。 C, D についても同様に 1, 1 となります。

問題の解説

今回の問題を振り返ります。

const (
    X       = 0                        // index: 0
    A, B, C = iota, iota + 1, iota * 2 // index: 1
    D, _, E                            // index: 2
    _, F, _                            // index: 3
    G       = iota + iota              // index: 4
)

ConstSpecのindexを書いてしまえば、一気に内容が理解しやすくなります。

A, B, C はConstSpecのindexが 1 なので、それぞれ 1, 1 + 1 => 2, 1 * 2 => 2 です。

D, E は、右辺を = iota, iota + 1, iota * 2 と書いているのと同じと見なされるので、それぞれ 2, 2 * 2 => 4 です。

F も同様に、 3 + 1 => 4 です。

最後に、Gについてですが、これもiotaがConstSpecのindexでしかないことを考慮すると 4 + 4 => 8 となります。

結果、 D + E + F + G2 + 4 + 4 + 818 となります。

言語仕様の該当箇所について

今回解説した内容は、

に書かれているので、時間のある時にぜひ読んでみてください。

*1:左辺側に現れる定数の型についても同様のルールが適用されますが、ここでは突っ込んで解説しません。