A Tour of Goをやるぞ(More types: structs, slices, and maps.)

これをやる

A Tour of Go

ポインタ

package main

import "fmt"

func main() {
    i := 10;
    p := &i
    fmt.Println(p) // ポインタ
    fmt.Println(*p) // 値
    *p = 20 // ポインタ経由での値変更
    fmt.Println(i)
 
    v := 20
    i = v
    fmt.Println(&i) // 代入しても参照先は変わらない
    fmt.Println(&v) // が、vとiの参照先は別
}

Javaの感覚で捉えてしまうと最後のiは参照先が変わりそうだが変わらなかった。 基本的には

  • &で参照先アドレスを取得
  • *で参照先アドレスから値を取得

ということで良さそうだが。 ポインタを直接操作することはできない。(Cでポインタにintegerの幅だけずらした値を足して配列の次の値をとる的なのがあった気がするがそういうのか?)

後ポインタに別の値のポインタを代入とかもだめ。

struct

フィールドの集合

structsへアクセスするポインタ

structへのアクセスは、変数にドット繋ぎでもできるし、ポインタにドット繋ぎでも可能。

package main

import "fmt"

type Structure struct {
    X int
    Y int
}

func main() {
    v := Structure{1, 2} // 初期化
    v.X = 100
    fmt.Println(v) // {100 2}

    p := &v
    p.X = 1000 // ポインタ.フィールド、で直接フィールドを参照できる
    fmt.Println(v) // {1000 2}
 
    (*p).X = 10000 // これでもいける
    fmt.Println(v) // {10000 2}
}

これポインタ経由でのアクセスかどうかに差ないのかな? ちょっと気持ち悪い気がするけどそもそもポインタを使いたくなるケースが見えてないのでなんとも言えない。

ArraysとSlices

変数の型としては同じものを指しているのかなと思ったが、

Slices are like references to arrays

とのこと。 ので、ArrayからSliceを取得してSliceの値をいじると元のArrayの値も変更される。

sliceのlengthとcapacity

length

sliceの要素数

capacity

sliceの最初の要素からsliceが参照してるarrayの末尾の要素までの要素数

capacityの範囲でsliceの要素は元のarrayに戻せる。

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    s = s[2:4]
    fmt.Println(s) // 5, 7
    fmt.Printf("%d %d \n",len(s), cap(s)) // 2, 4

    s = s[0:]
    fmt.Println(s) // 5, 7(これはcapacityの範囲ではなくlengthの範囲でのsliceになる)
    fmt.Printf("%d %d \n",len(s), cap(s)) // 2, 4

    s = s[0:4]
    fmt.Println(s) // 5, 7, 11, 13
    fmt.Printf("%d %d \n",len(s), cap(s)) // 4, 4
}

built-in関数make

a := make([]int, 3) // len(a) = 3, cap(a) = 3
b := make([]int, 3, 5) // len(a) = 3, cap(a) = 5

rangeによるイテレーション

func main() {
    values := []int{1,2,3}
    for index, value := range values {
        fmt.Println("index:%d, value:%d", index, value)
    }
}

rangeでmapとsliceのイテレーションを提供する。sliceの場合indexとvalue

Exercise: Slices

package main

import "golang.org/x/tour/pic"

func Pic(dx, dy int) [][]uint8 {
    vals := make([][]uint8, dy)
    for i,_ := range vals {
        vals[i] = make([]uint8, dx)
        for j,_ := range vals[i] {
            vals[i][j] = uint8((int(i) + int(j))/2)
        }
    }
    return vals
}

func main() {
    pic.Show(Pic)
}

グラデーションになった。

Map

rubyのhashみたいな感じ。

package main

import "fmt"

type Test struct {
    test string
}

type Key struct {
    test string
}

var key1 = Key{"key1"}
var key2 = Key{"key2"}

var m = map[Key]Test{
    key1: Test{"test1"},
    key2: Test{"test2"},
}

func main() {
    fmt.Println(m)
    fmt.Println(m[key1])
}

これでもいい。

var m = map[Key]Test{
    key1: {"test1"},
    key2: {"test2"},
}

Mapの操作

package main

import "fmt"

func main() {
    m := make(map[string]string)
    m["key1"] = "value1"
    val := m["key1"]
    fmt.Println(val)
 
    val1, ok1 :=  m["key1"]
    fmt.Printf("val:%d, ok:%s\n" , val1, ok1)

    val2, ok2 :=  m["key2"]
    fmt.Printf("val:%d, ok:%s\n" , val2, ok2)
}

2つ目の戻り値でkeyに対応する値の有無を取得できる。

Exercise: Maps

package main

import (
    "golang.org/x/tour/wc"
    "strings"
)

func WordCount(s string) map[string]int {
    words := strings.Fields(s)
    counter := make(map[string]int)
    for _, value := range words {
        count, ok := counter[value]
        if ok {
            counter[value] = count + 1
        } else {
            counter[value] = 1
        }
    }
    return counter
}

func main() {
    wc.Test(WordCount)
}

でもこのケースだとmapのvalueは初期化時に0でいいので

package main

import (
    "golang.org/x/tour/wc"
    "strings"
)

func WordCount(s string) map[string]int {
    words := strings.Fields(s)
    counter := make(map[string]int)
    for _, value := range words {
        counter[value]++
    }
    return counter
}

func main() {
    wc.Test(WordCount)
}

でいい。

Exercise: Fibonacci closure

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
    index := 0
    first := 0
    second := 1
    return func() int {
        index++
        switch index {
            case 1: return 0
            case 2: return 1
        }
        now := first + second
        first = second
        second = now
        return now
    }
}

func main() {
    f := fibonacci()
    for i := 0; i < 10; i++ {
        fmt.Println(f())
    }
}

ここにシュッとした回答がいっぱいある。

感想

  • ポインタ周りの話があまりわかってない(ユースケースとか)
  • sliceのcapacityの存在意義がわかってない(JavaArrayListのcapacityと同じ程度の感覚でいいのかな)
  • クロージャは関数オブジェクトの中にローカル変数を持てるやつ