読者です 読者をやめる 読者になる 読者になる

備忘録

なんとなく暇なときにでも....

A Tour of Go を1週間かけてやりました。

最近golangの記事をちょちょく挙げさせていただいているように最近golangの勉強をしています。
それにも関わらず、実はかの有名な「A Tour of Go」をやっていないことに気づいたので、1週間ほど少しづつ進めて一通りなぞったので、完全に記録でしかありませんが、自分へのメモとして挙げておきます。

3.15

Defer (https://go-tour-jp.appspot.com/flowcontrol/12)

  • defer ステートメントは、 defer へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させるものです。
  • defer へ渡した関数の引数は、すぐに評価されますが、その関数自体は呼び出し元の関数がreturnするまで実行されません。→ この呼び出しは関数スコープを離れる時(クエリ関数から戻る時)に実行されます。
  • defer へ渡した関数が複数ある場合、その呼び出しはスタック( stack )されます。 呼び出し元の関数がreturnするとき、 defer へ渡した関数は LIFO(last-in-first-out) の順番で実行されます。

3.16

Pointers to structs (https://go-tour-jp.appspot.com/moretypes/4)

  • structのフィールドは、structのポインタを通してアクセスすることできます。
  • スライスは配列への参照のようなものです (https://go-tour-jp.appspot.com/moretypes/8)

    スライスはどんなデータも格納しておらず、単に元の配列の部分列を指し示しています。 スライスの要素を変更すると、その元となる配列の対応する要素が変更されます。 同じ元となる配列を共有している他のスライスは、それらの変更が反映されます。

Slice literals (https://go-tour-jp.appspot.com/moretypes/9)

s := []struct {
    i int
    b bool
}{
    {2, true},
    {3, false},
    {5, true},
    {7, true},
    {11, false},
    {13, true},
}
fmt.Println(s)
//[{2 true} {3 false} {5 true} {7 true} {11 false} {13 true}]

3.17

Function values (https://go-tour-jp.appspot.com/moretypes/24)

関数も変数です。他の変数のように関数を渡すことができます。 関数値( function value )は、関数の引数に取ることもできますし、戻り値としても利用できます。

func compute(fn func(float64, float64) float64) float64 {
    return fn(3, 4)
}

func main() {
    hypot := func(x, y float64) float64 {
        return math.Sqrt(x*x + y*y)
    }
    fmt.Println(hypot(5, 12)) //13

    fmt.Println(compute(hypot)) //5
    fmt.Println(compute(math.Pow)) // 81
}

3.18

Methods (https://go-tour-jp.appspot.com/methods/1)

Goには、クラス( class )のしくみはありませんが、型にメソッド( method )を定義できます。 メソッドは、特別なレシーバ( receiver )引数を関数に取ります。 レシーバは、 func キーワードとメソッド名の間に自身の引数リストで表現します。 この例では、 Abs メソッドは v という名前の Vertex 型のレシーバを持つことを意味しています。

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 { // レシーバを(v Vertex)として設定できる
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs())
}

Pointer receivers (https://go-tour-jp.appspot.com/methods/4)

ポインタレシーバでメソッドを宣言できます。 ポインタレシーバを持つメソッド(ここでは Scale )は、レシーバが指す変数を変更できます。 レシーバ自身を更新することが多いため、変数レシーバよりもポインタレシーバの方が一般的です。

func (v *Vertex) Scale(f float64) {
    v.X = v.X * f
    v.Y = v.Y * f
}
  • メソッドがポインタレシーバである場合、呼び出し時に、変数、または、ポインタのいずれかのレシーバとして取ることができます: go var v Vertex v.Scale(5) // OK p := &v p.Scale(10) // OK

    v.Scale(5) のステートメントでは、 v は変数であり、ポインタではありません。 メソッドでポインタレシーバが自動的に呼びだされます。 Scale メソッドはポインタレシーバを持つ場合、Goは利便性のため、 v.Scale(5) のステートメントを (&v).Scale(5) として解釈します。

  • メソッドが変数レシーバである場合、呼び出し時に、変数、または、ポインタのいずれかのレシーバとして取ることができます: go var v Vertex fmt.Println(v.Abs()) // OK p := &v fmt.Println(p.Abs()) // OK

    この場合、 p.Abs() は (*p).Abs() として解釈されます。

  • ここまで!


3.19

Choosing a value or pointer receiver (https://go-tour-jp.appspot.com/methods/8)

  1. メソッドがレシーバが指す先の変数を変更するためです。
  2. メソッドの呼び出し毎に変数のコピーを避けるためです。 例えば、レシーバが大きな構造体である場合に効率的です。

一般的には、変数レシーバ、または、ポインタレシーバのどちらかですべてのメソッドを与え、混在させるべきではありません。 (この理由は数ページ後にわかります)


3.20

The empty interface (https://tour.golang.org/methods/14)

ゼロ個のメソッドを指定されたインターフェース型は、 空のインターフェース と呼ばれます: interface{} 空のインターフェースは、任意の型の値を保持できます。 (全ての型は、少なくともゼロ個のメソッドを実装しています。) 空のインターフェースは、未知の型の値を扱うコードで使用されます。

func main() {
    var i interface{}
    describe(i)
    // (<nil>, <nil>)

    i = 42
    describe(i)
    //(42, int)

    i = "hello"
    describe(i)
    //(hello, string)
}

Type assertions (https://go-tour-jp.appspot.com/methods/15)

アサーション は、インターフェースの値の基になる具体的な値を利用する手段を提供します。 t := i.(T)

ここまで!


3.21

ここまで!


3.22

ここまで!


3.24,25

Channels (https://go-tour-jp.appspot.com/concurrency/2)

チャネル( Channel )型は、チャネルオペレータの <- を用いて値の送受信ができる通り道です。

ch <- v    // v をチャネル ch へ送信する
v := <-ch  // ch から受信した変数を v へ割り当てる
           // (データは、矢印の方向に流れます)

マップとスライスのように、チャネルは使う前に以下のように生成します:

ch := make(chan int)

Buffered Channels (https://go-tour-jp.appspot.com/concurrency/3)

チャネルは、 バッファ ( buffer )として使えます。 バッファを持つチャネルを初期化するには、 make の2つ目の引数にバッファの長さを与えます ch := make(chan int, 100)

func main() {
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    ch <- 3
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}
/**
* fatal error: all goroutines are asleep - deadlock!
* 
* goroutine 1 [chan send]:
* main.main()
* /tmp/sandbox573738310/main.go:9 +0x100
**/

Select (https://go-tour-jp.appspot.com/concurrency/5)

select ステートメントは、goroutineを複数の通信操作で待たせます。

select は、複数ある case のいずれかが準備できるようになるまでブロックし、準備ができた case を実行します。 もし、複数の case の準備ができている場合、 case はランダムに選択されます。

func fibonacci(c, quit chan int) {
    x, y := 0, 1
    for {
        select {
        case c <- x:
            x, y = y, x+y
        case <-quit:
            fmt.Println("quit")
            return
        }
    }
}

func main() {
    c := make(chan int)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println(<-c)
        }
        quit <- 0
    }()
    fibonacci(c, quit)
}
/**
0
1
1
2
3
5
8
13
21
34
quit
**/


本当にメモです….。


本日はここまで