LoginSignup
4

More than 1 year has passed since last update.

posted at

updated at

Organization

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

はじめに

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
    }
}

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
What you can do with signing up
4