Skip to content

Instantly share code, notes, and snippets.

@su-kun1899
Last active October 2, 2017 23:49
Show Gist options
  • Save su-kun1899/8ee8a0ffa2e4a6aef4551fc561b4ce07 to your computer and use it in GitHub Desktop.
Save su-kun1899/8ee8a0ffa2e4a6aef4551fc561b4ce07 to your computer and use it in GitHub Desktop.
Tour of Goのメモ

For

	for i := 0; i < 10; i++ {
		sum += i
	}

初期化と後処理は省略可

	sum := 1
	for ; sum < 1000; {
		sum += sum
	}

セミコロンも省略可(while文相当)

	sum := 1
	for sum < 1000 {
		sum += sum
	}

条件を省略すれば無限ループになる

	for {
	}

if

	i := 5
	if i < 10 {
		fmt.Printf("i: %d\n", i)
	}

条件の前に一文書ける

変数のスコープはifブロックになる(elseでも使える)

	j := 5
	if i := 10; j < i {
		fmt.Printf("i, j: %d, %d\n", i, j)
	} else {
    fmt.Printf("i: %d\n", i)
  }

switch

  • breakがいらない(書かなくてもbreakする)
  • fallthroughを書くとbreakしない
	fmt.Print("Go runs on ")
	switch os := runtime.GOOS; os {
	case "darwin":
		fmt.Println("OS X.")
	case "linux":
		fmt.Println("Linux.")
	default:
		// freebsd, openbsd,
		// plan9, windows...
		fmt.Printf("%s.", os)
	}
  • 条件を省略することもできる
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("Good morning!")
	case t.Hour() < 17:
		fmt.Println("Good afternoon.")
	default:
		fmt.Println("Good evening.")
	}

defer

  • 関数をスタックしておける
  • deferした関数は呼び出し元がreturn下タイミングで呼ばれる
  • 複数deferしている場合LIFO(last-in-first-out)になる
// counting
// done
// 9
// 8
// 7...
func main() {
	fmt.Println("counting")

	for i := 0; i < 10; i++ {
		defer fmt.Println(i)
	}

	fmt.Println("done")
}

ポインタ

func main() {
	i := 42

	p := &i         // iのポインタを引き出してpに代入する
	fmt.Println(*p) // ポインタ経由でiを読み出す
	*p = 21         // ポインタ経由でiの値を書き換える
	fmt.Println(i)  // iの値が変わっている
}

構造体

type Vertex struct {
	X int
	Y int
}

func main() {
	v := Vertex{1, 2}
	v.X = 4
	fmt.Println(v.X) // 4
	fmt.Println(v.Y) // 2
}

名前付き初期化

v := Vertex{Y:1, X:2}

// 名前をつけると初期値を省略できる
v2 := Vertex{X:2}

ポインタ経由でのアクセス

func main() {
	v := Vertex{1, 2}
	p := &v
	p.X = 1e9
	fmt.Println(v) // {1000000000 1}
}

Arrays & Slices

// Arrays
var a [2]string
a[0] = "Hello"
a[1] = "World"

primes := [6]int{2, 3, 5, 7, 11, 13}

// slices {3, 5, 7}
// Sliceは配列への参照なので、変更は同じ参照すべてに反映される
var s []int = primes[1:4]

q := []int{2, 3, 5, 7, 11, 13}
s := []struct {
	i int
	b bool
}{
	{2, true},
	{3, false},
	{5, true},
	{7, true},
	{11, false},
	{13, true},
}

下記のSliceはすべて同じ意味

var a [10]int
a[0:10]
a[:10]
a[0:]
a[:]

スライスの長さ(要素数): length 容量: capacity

s := []int{2, 3, 5, 7, 11, 13}
// -> [3,5,7]
s = s[1:4]
// length(3)
fmt.Println(len(s))
// capacity(6)
fmt.Println(cap(s))

// -> [7]
s = s[2:]
// length(1)
fmt.Println(len(s))
// capacity(4)
fmt.Println(cap(s))

Sliceはmake関数で作成できる 配列を割り当て、そのSliceを返す

// len: 5
a := make([]int, 5)
// 第3引数はcap
b := make([]int, 0, 5)

要素の追加はappendで行う サイズの割り当て直しもやってくれる

s := []int{3,5,2}
s = append(s, 4, 4, 2)
// s: [3, 5, 2, 4, 4, 2]

Range

SliceやMapの反復処理

s := []string{"hoge", "fuga", "piyo"}
for index, item := range s {
	fmt.Printf("index: %d, item: %s\n", index, item)
}

valueは省略可

s := []string{"hoge", "fuga", "piyo"}
for index := range s {
	fmt.Printf("index: %d\n", index)
}

indexを捨てる場合はアンダーバーを使う

s := []string{"hoge", "fuga", "piyo"}
for _, item := range s {
	fmt.Printf("item: %s\n", item)
}

Maps

var hoge map[string]int
hoge = make(map[string]int)
hoge["test"] = 13
fmt.Println(hoge["test"])

こんな風にも書ける

var fuga = map[int]string{
	100: "high",
	50: "middle",
	10: "low",
}

構造体の場合型名を略せる

type Vertex struct {
	Lat, Long float64
}

var m = map[string]Vertex{
	"Bell Labs": {40.68433, -74.39967},
	"Google":    {37.42202, -122.08408},
}

要素の削除

// delete(map, key)
delete(hoge, "test")

要素の存在チェック

// 二つ目の値にtrue/falseが格納される
elem, ok := m[key]

文字列の出現回数をカウントする

package main

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

func WordCount(s string) map[string]int {
	fields := strings.Fields(s)
	fmt.Println("Fields are: %q", fields)
	
	var wordCounts map[string]int
	wordCounts = make(map[string]int)
	for i := 0; i < len(fields); i++ {
		word := fields[i]
		count, exists := wordCounts[word]
		
		if exists {
			wordCounts[word] = count + 1
		} else {
			wordCounts[word] = 1
		}
	}
	
	return wordCounts
}

Function values

関数は変数、引数、戻り値にできる

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

func main() {
	sum := func(x, y float64) float64 {
		return x + y
	}
	fmt.Println(sum(5, 12)) // 17
	fmt.Println(compute(sum)) // 7
}

Function closures

  • 関数オブジェクト
  • 変数を関数スコープで解決する
func adder() func(int) int {
	sum := 0
	return func(x int) int {
		sum += x
		return sum
	}
}

フィボナッチ関数

package main

import "fmt"

// fibonacci is a function that returns
// a function that returns an int.
func fibonacci() func() int {
	prev := 0
	current := 0
	next := 0
	
	return func() int {
		if (prev == 0 && current == 0) {
			prev = 1
			return 0
		} 
		next = prev + current
		prev = current
		current = next
		return next
	}
}

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

Methods

型にメソッドを追加 レシーバを伴うメソッドの宣言は、レシーバ型が同じパッケージにある必要がある

type Vertex struct {
	X, Y float64
}

// レシーバにメソッドを追加したい型を定義
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

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

任意の型にもメソッドを宣言できる

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

レシーバ自身の値を操作するようなメソッドの場合は、ポインタレシーバを使用する

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// Vertex自身の値を書き換えるので、ポインタレシーバを使う
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

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

Interfaces

Interfaceは暗黙的に実装されるため、明示的に実装を宣言する必要はない。

// 宣言
type Abser interface {
	Abs() float64
}

type Vertex struct {
	X, Y float64
}

// 実装: 変数レシーバである必要がある
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

インターフェース自体の中にある具体的な値が nil の場合、メソッドは nil をレシーバーとして呼び出す。
Go では nil をレシーバーとして呼び出されても適切に処理するメソッドを記述するのが一般的らしい。

type I interface {
	M()
}

type T struct {
	S string
}

// nilの場合文字列を出力する
func (t *T) M() {
	if t == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(t.S)
}

空のインターフェースは任意の型として利用できる

func main() {
	var i interface{}
	describe(i)

	i = 42
	describe(i)

	i = "hello"
	describe(i)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

インターフェースの型を調べるにはType assertionsを使う。

func main() {
	var i interface{} = "hello"

  // okに型チェック結果が格納される
	s, ok := i.(string)

	// 型チェックがfalseの場合、fはゼロ値になる
	f, ok := i.(float64)
}

Type switchesを使うと、複数の型チェックをまとめてできる。

func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main() {
	do(21)
	do("hello")
	do(true)
}

Stringers

文字列を fmt パッケージ に定義されている Stringer インターフェースはstringとして表現することができる型である。

type Stringer interface {
    String() string
}

JavaのtoString的扱いに使える?

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

IPAddr 型を実装

package main

import (
	"fmt"
	"strings"
)

type IPAddr [4]byte

func (addr IPAddr) String() string{	
	var sum []string
	for i := 0; i < len(addr); i++ {
		//sum += fmt.Sprint(addr[i])
		sum = append(sum, fmt.Sprint(addr[i]))
	}
	
	return strings.Join(sum, ".")
}

func main() {
	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}

Errors

Error型はfmt.Stringer に似た組み込みのインタフェース。

type error interface {
    Error() string
}

関数にエラーを返させ、呼び出し元でハンドリングするのがお作法?

i, err := strconv.Atoi("42")
if err != nil {
    fmt.Printf("couldn't convert number: %v\n", err)
    return
}
fmt.Println("Converted integer:", i)

Readers

データを与えられたバイトスライスへ入れ、入れたバイトのサイズとエラーの値を返すインターフェース。

ストリームの終端は、 io.EOF のエラーで返す。

func (T) Read(b []byte) (n int, err error)

strings.NewReaderを使って文字列を8バイト毎に読み出す。

func main() {
	r := strings.NewReader("Hello, Reader!")

	b := make([]byte, 8)
	for {
		n, err := r.Read(b)
		fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
		fmt.Printf("b[:n] = %q\n", b[:n])
		if err == io.EOF {
			break
		}
	}
}

Goroutines

軽量なスレッド

go f(x, y, z) で記述する。

func say(s string) {
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main() {
	// 別スレッドで実行される
	go say("world")

	say("hello")
}

Channels

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

  • ch <- v : v をチャネル ch へ送信する
  • v := <-ch : ch から受信した変数を v へ割り当てる
  • ch := make(chan int) : チャネルを生成する
func sum(s []int, c chan int) {
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum // send sum to c
}

func main() {
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c // receive from c

	fmt.Println(x, y, x+y) // -5 17 12
}

Buffered Channels

チャネルはバッファを持たせられる。

  • バッファが詰まった時は、チャネルへの送信をブロックする
  • バッファが空の時には、チャネルの受信をブロックする
  • いずれも fatal error: all goroutines are asleep - deadlock! になる
ch := make(chan int, 100)

Range and Close

送り手はチャネルをクローズできる

  • 受け手は二つ目の戻り値でクローズしているか判断できる
  • 送り手のチャネルだけをcloseする(受け手はcloseしない)
    • closeしたチャネルへ送信すると、パニック( panic )になる
// 受信する値がない、かつ、チャネルが閉じているなら、 ok は false
v, ok := <-ch
  • Rangeを使うとチャネルが閉じられるまで、チャネルから値を繰り返し受信し続けられる
for i := range c
  • チャネルは、通常closeする必要はない
  • closeするのは、これ以上値が来ないことを受け手が知る必要があるときだけ
  • 例えば、 range ループを終了する場合
func fibonacci(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}

Select

select ステートメントは、goroutineを複数の通信操作で待つ

  • 複数ある 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)
}

どの case も準備ができていないのであれば、 select の中の default が実行される。

select {
case i := <-c:
    // use i
default:
    // receiving from c would block
}

sync.Mutex

MutexのLockとUnlockを使うと排他制御が行える。

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
	v   map[string]int
	mux sync.Mutex
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
	c.mux.Lock()
	// Lock so only one goroutine at a time can access the map c.v.
	c.v[key]++
	c.mux.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
	c.mux.Lock()
	// Lock so only one goroutine at a time can access the map c.v.
	defer c.mux.Unlock()
	return c.v[key]
}

func main() {
	c := SafeCounter{v: make(map[string]int)}
	for i := 0; i < 1000; i++ {
		go c.Inc("somekey")
	}

	time.Sleep(time.Second)
	fmt.Println(c.Value("somekey"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment