Open12

Go学習メモ

hakshuhakshu

Go言語を学習する時に見たものや学んだことを書き残していく

hakshuhakshu

packages

Javaなどと同様にpackageがある
他のpackageをインポートして使うことができる
package mainがエントリポイントになる

Imports

package main

// 一つずつimportを書けるが
import "fmt"
import "math"
package main

// 以下のようにimportをまとめられる
import (
  "fmt"
  "math"
)

Export

Goでメソッドなどを公開する場合はメソッド名を大文字で始める
パッケージ外から大文字で始まるもの以外は参照・アクセスできない

fmt.Println("Exported!")
hakshuhakshu

Functions

関数は引数を0以上持てる
宣言はfuncで始める

func add(x int, y int) int {
  return x + y
}
// 以下のように引数の型が同じならまとめることもできる
func add(x, y int) int {
  return x + y
}

複数の返り値を返せる

func swap(a, b string) (string, string) {
  return b, a
}

返り値には名前をつけることもできる
この場合returnに返り値を渡さずとも対応した変数が返される
ただ、長い関数だと可読性が悪くなるので使い所は注意

func split(sum int) (x, y int) {
  x = sum * 4 / 7
  y = sum - x
  return
}
hakshuhakshu

Variables

変数はvarで宣言する
変数は関数の中でもトップレベルでも宣言できる
型は変数の後につける

var x int
// 初期化
var y int = 3

// shorthand
// := でvarと型宣言を省略する、型は右辺から型推論される
z := 12

Constants

定数はconstで宣言する
:=は使えない

const Pi = 3.14
hakshuhakshu

Basic types

プリミティブ型は以下

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8のalias
rune // int32のalias
float32 float64
complex64 complex128

初期値は数値系は0、boolはfalse、stringは""

型変換

キャストする場合はType(variable)のようにする

var x int = 36
var y float64 = float64(x)
hakshuhakshu

for

JavaやJavascriptに近い
ただ、ループの条件は()でくくらない

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

// 最初と最後の式を省略してwhileのようにも扱える
// 比較式も省略すると無限ループになる
sum := 1
for sum < 10000 {
  sum += sum
}

if

forと同様に条件を()でくくらない
他条件をつける場合はelse if、またelseを使える

if x < 0 {
  return
}

// 条件の前に式を入れることができる
if v := math.Pow(x, n); v < lim {
  return v
}

switch

breakを明示的に書かなくても条件を満たすcaseで実行は止まる

x := 0
if x < 100 {
  switch x += 1; x {
    case x % 15 == 0:
      fmt.Println("fizzbuzz")
    case x % 3 == 0:
      fmt.Println("fizz")
    case x % 5 == 0:
      fmt.Println("buzz")
    default:
      fmt.Println(x)
  }
}
hakshuhakshu

defer

実行した関数がreturnするまで、遅延して実行できる
複数deferがある場合はstackのようにLIFOになっている

func main() {
  defer fmt.Println("world")

  fmt.Println("Hello")
}
hakshuhakshu

Pointer

ポインタ型は型に*をつける
初期値はnil
ポインタ演算は無い

var p *int

// 変数からポインタを取り出したい場合は`&`を変数につける
x := 12
p = &x

// ポインタに格納された値を取得する場合
fmt.Println(*p)
// ポインタを使って値を格納する場合
*p = 36

Struct

構造体

type Vector struct {
  X, Y int
}

func main() {
  v := Vector{2, 4}
  v2: = Vector{X: 1, Y: 2}
  v3 := Vector{} // X = 0, Y = 0
  fmt.Println(v)
  v.X = 10
  fmt.Println(v)
  // ポインタアクセスする場合は*を省略できる
  p := &v
  p.Y = 20
  fmt.Println(v)
}

Array

変数宣言時に配列のサイズが決まり、変更することはできない

var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a)

Slice

サイズを動的に変更できる配列
Sliceは元の配列への参照を持つため、値を変更すると元の配列の値も変更される
Sliceの初期値はnil

list := [6]int{1, 2, 3, 4, 5, 6}
var s []int = list[1:4]
fmt.Println(s)
// lenとcapでそれぞれSliceの長さと容量を取得できる
fmt.Printf("len=%d cap=%d", len(s), cap(s))

// makeという組み込み関数で0埋めしたSliceを生成することもできる
a := make([]int, 10)
fmt.Println(a)

// appendで値を追加できる
a = append(a, 1)
fmt.Println(a)

Map

連想配列
初期値はnil

m = make(map[string]int) // [keyの型]valueの型
m["Japan"] = 47
fmt.Println(m)

Range

SliceやMapをforで扱うのに使えるrangeというものがある
各要素のindexとvalueをiterableに取り出す

var s = []int{1, 2, 4}
for i, v := range s {
  fmt.Printf("i: %d, v: %d", i, v)
}
hakshuhakshu

Method

GoはClassは無いが型に対してメソッドを定義できる
メソッドを定義できるのは同じパッケージで定義されている型のみ

type Person struct {
  firstName, lastName string
  age int
}

func (p Person) fullName() string {
  return firstName + lastName
}

// ポインタをレシーバにもできる
func (p *Person) growOld() {
  p.age += 1
}

func main() {
  p := Person{"太郎", "山田", 20}
  fmt.Println(p.fullName())
}

Interface

Javaなどのinterfaceと同様に実装するメソッドを定義する
この型を宣言した変数にinterfaceで定義したメソッドが無いとエラーになる

type Abser interface {
  Abs() float64
}

// 空のinterface型はTypeScriptで言うところのany型になる
var i interface{}

Type assertion

型をassertしたい場合は以下のようにする
t, ok := i.(T)

hakshuhakshu

Errors

組み込みでエラーを表す値でerrorがある
型は以下のようになっている

type error interface {
  Error() string
}
hakshuhakshu

Goroutines

  • Goでの並行処理を実現するための仕組み
  • 軽量スレッド
  • 関数呼び出しの前にgoキーワードをつけると生成される
  • 同じアドレス空間を共有するので、共有メモリの扱いには気をつける
func main() {
  go func() {
    fmt.Println("別goroutine")
  }()
  time.Sleep(100)
  fmt.Println("main goroutine")
}

Channels

  • goroutine間でデータをやり取りするための仕組み
    • 共通の変数を参照するようにすると競合してしまう
  • 送受信できるデータの型は宣言時に指定する
  • Channelはバッファを持たせることができる
    • 指定しないと容量0
  • 送信時にチャネルのバッファがいっぱいだとブロックする
  • 受信時にチャネルが空だとブロックする
  • 送信側からcloseすることもできる
// 初期化
ch1 := make(chan int, 10)

// チャンネルに送信
ch1 <- 10

// チャンネルから受信
v1 := <- ch1

// fotとselectで複数のgoroutingとの通信を扱う
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)
}