LoginSignup
6
4

More than 3 years have passed since last update.

A Tour of Go メモ ~More types: structs, slices, and maps~

Last updated at Posted at 2020-12-08

はじめに

Go の勉強のため、A Tour of Goに取り組んでいる
今回はMore types: structs, slices, and maps章について、学んだことを記していく

まとめ記事はこちら

使えそうなスニペット

※筆者は VSCode を使用

tys

名前、フィールドの順にカーソルが移動する

type name struct {

}

forr

インデックス、要素、イテレータの順にカーソルが移動する

for _, var := range var {

}

struct や map の初期化の際、型を補完してくれる

これはなかなか便利
image.png

ページごとの補足

Pointers

ポインタを理解するのに、この記事が参考になった
ざっくりした理解ではあるが、

  • オペランド(変数および値)はメモリのどこかに格納されている
  • ポインタとは、オペランドを格納しているメモリのアドレス
  • &オペレータは、そのオペランドのポインタを示す
  • *オペレータは、そのポインタの指す先の変数を示す
i := 10
fmt.Println(i) // 10
fmt.Println(&i) // 0xc000012080
fmt.Println(*&i) // 10

Arrays

配列とは

Go の配列は値である
配列変数は配列全体を示す(C 言語とは違う)
配列は、 名前付きフィールドではなくインデックスフィールドを使用できる構造体の一種と考えると腹落ちしやすかった

コンパイラに配列の要素数をカウントさせる

 // 以下の 2 つは同じ型になる
    a := [6]int{2, 3, 5, 7, 11, 13}
  b := [...]int{2, 3, 5, 7, 11, 13}

初期値

初期化時に与えた引数より配列のサイズが大きい場合、ゼロ値が代入される

primes := [10]int{2, 3, 5, 7, 11, 13}
fmt.Println(primes) // [2 3 5 7 11 13 0 0 0 0]

Slice length and capacity

Go Slices: usage and internalsが非常に分かりやすい

スライスは、

  • 配列へのポインタ
  • スライスによって参照される要素の数
  • 容量は、基になる配列の要素の数

で構成される、と考えると理解しやすかった

a := [6]int{2, 3, 5, 7, 11, 13}
s := a[:]
printSlice(s) // len=6, cap=6, [2 3 5 7 11 13]

s = s[:0]     // 参照される要素の数を変更
printSlice(s) // len=0, cap=6, []

s = s[:4]     // 参照される要素の数を変更
printSlice(s) // len=4, cap=6, [2 3 5 7]

s = s[2:]     // ポインタを s[0] から s[2]に移動する == その分、参照できるlen, capが減る
printSlice(s) // len=2, cap=4, [5 7]

s = s[:4]     // ポインタを移動した状態で、参照される要素の数を変更
printSlice(s) // len=4, cap=4, [5 7 11 13]

func printSlice(s []int) {
    fmt.Printf("len=%d, cap=%d, %v\n", len(s), cap(s), s)
}

スライスの容量を越えて延ばすと、panic: runtime error: slice bounds out of range [:5] with capacity 4が発生する

Creating a slice with make

slice はmake関数でも定義できる
定義: func make([]T, len, cap) []T
容量(cap)を省略すると、長さ(len)と同じになる

// 同じ値を定義
fmt.Println(make([]int, 5, 5))
fmt.Println(make([]int, 5))
fmt.Println([]int{0, 0, 0, 0, 0})

Slices of slices

サンプル通りに書くと、redundant type from array, slice, or map compositeと警告が出る
入れ子の場合は内部の型を省略できる

// not very well
board := [][]string{
  []string{"_", "_", "_"},
  []string{"_", "_", "_"},
  []string{"_", "_", "_"},
}
// well
board := [][]string{
  {"_", "_", "_"},
  {"_", "_", "_"},
  {"_", "_", "_"},
}

Appending to a slice

もし、元の配列 s が、変数群を追加する際に容量が小さい場合は、より大きいサイズの配列を割り当て直す
再割当てにはコストがかかるため、予め多くを append することが分かっているなら、必要な分だけ容量を確保したほうが良い

var s []int // 20ms: 初期容量は0
s = make([]int, 0, 1000000) // 7ms: 初期容量は1000000
for i := 0; i < 1000000; i++ {
  s = append(s, 0)
}

ちなみに、時間測定の方法はこちらが参考になった

func elapsed() func() {
    start := time.Now()
    return func() {
        fmt.Printf("%v", time.Since(start))
    }

}
func main() {
    defer elapsed()()
}

Exercise: Slices

パッケージインストール

手元で動かす場合は "golang.org/x/tour/pic"をインストール必要がある
そのまま get しても良いが、Go Moduleを使ってローカルに get してみた
Ref. 新規プロジェクトの作りかたがわからない

$ go mod init {PROJECT_NAME}
 go get golang.org/x/tour

こうすると、go.modが作成され、get したパッケージのバージョン情報が記述される

module github.com/eyuta/goTour

go 1.15

require golang.org/x/tour v0.0.0-20201207214521-004403599411

実装

インデックスで挿入する場合は、

func Pic(dx, dy int) [][]uint8 {
    exp := make([][]uint8, dx)
    for i := 0; i < dx; i++ {
        inner := make([]uint8, dy)
        for j := 0; j < dy; j++ {
            inner[j] = uint8(i * j)
        }
        exp[i] = inner
    }
    return exp
}

append を使う場合、

func Pic(dx, dy int) [][]uint8 {
    exp := make([][]uint8, 0, dx)
    for i := 0; i < dx; i++ {
        inner := make([]uint8, 0, dy)
        for j := 0; j < dy; j++ {
            inner = append(inner, uint8(i*j))
        }
        exp = append(exp, inner)
    }
    return exp
}

尚、スライスの定義を[][]uint8{}といったような容量を 0 にして定義した場合、若干速度が遅くなる
(Appending to a slice参照)

初期容量=dx, dy: 22ms ~ 24ms
初期容量=0 : 24ms ~ 28ms

インデックスと append では速度の差はほとんどなかった

Exercise: Maps

makeするの忘れがちなので注意する

func WordCount(s string) (result map[string]int) {
    result = make(map[string]int)
    for _, field := range strings.Fields(s) {
        elem, ok := result[field]
        switch ok {
        case true:
            result[field] = elem + 1
        case false:
            result[field] = 1
        }
    }
    return
}

Exercise: Fibonacci closure

func fibonacci() func() int {
    x, y := 0, 1
    return func() int {
        result := x
        x, y = y, x+y
        return result
    }
}
6
4
0

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
6
4