Golang

Packages, Valiables and Functions

Packages

  • Go のプログラムはパッケージで成り立っている
  • mainパッケージのmain関数からプログラムが始まる
  • パッケージ名はインポートパスの最後の要素と同じ名前である(math/randならrand)

Imports

// bad
import "fmt"
import "math"

// good (factored import)
import (
  "fmt"
  "math"
)

Exported names

  • 大文字で始まる名前がエクスポートされる
  • パッケージをインポートしたとき、エクスポートされた名前のみ参照できる

Functions

  • 各引数の後に型を記載する
  • ()の後に返値の型を記載する
func add(x int, y int) int {
  return x + y
}

// 連続する引数が同じタイプなら省略できる
func add(x, y int) int {
  return x + y
}

Multiple results

ファンクションは複数の値を返すことができる。

func swap(x, y string) (string, string) {
  return y, x
}

func main() {
  a, b := swap("hello", "world")
  fmt.Println(a, b) // => world hello
}

Named return values

  • 戻り値に名前をつけると、関数の最初で定義した変数として扱われる
  • 名前付き戻り値を使い、かつ return に何も書かなかった場合、自動的に名前付き戻り値がリターンされる(naked return)。ただし、長い関数では読みやすさの点から明示的に記載すること。
  • 戻り値の意味を表す名前をつけるよう心がけること
func split(sum int) (x, y int) {
  x = sum / 2
  y = sum - x
  return
}

func main() {
  a, b := split(20)
  fmt.Println(a, b)
}

Variables

varで変数を宣言できる。

// 複数の同じ型の変数はまとめて宣言できる。
var a, b, c bool

func main() {
  var d int
  fmt.Println(a, b, c, d) // => false false false 0
}

Initializers

宣言時に内容を設定する場合は、型定義を省略できる。

var a, b, c = 1, true, "no!"
// or factored style
var (
  a = 1
  b = true
  c = no
)

fmt.Println(a, b, c) // => 1 true "no!"

Short variable declarations

  • ファンクションの中であれば、:=を使うことでvarを省略できる。
  • ファンクションの外では、すべての Statement はvar,func,importなどのキーワードから始まる必要があるため、省略形は使えない。
func someFunc() {
  a, b, c := 1, true, "no!"
  fmt.Println(a, b, c) // => 1 true "no!"
}

Basic types

  • int, uint, uintptrは、32bit システムでは 32bit、64bit システムでは 64bit である。
  • 特に理由がなければ、サイズ指定やアンサインドは使わず、intを使うこと。
bool

string

int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr (u = unsigned)

byte // alias for uint8

rune // alias for int32
     // represents a Unicode code point

float32 float64

complex64 complex128

Zero values

明示的に初期値を設定しない限り、変数には「Zero Values」がセットされる

  • 数値なら 0
  • boolean なら false
  • 文字列なら""(空文字列)
  • ポインタならnil
  • スライスならnil
  • マップならnil
var a int     // => 0
var b float64 // => 0
var c bool    // => false
var d string  // => ""

Type conversions

Type(value)の形で型変換を行える。

var i float64 = float64(123)
j := uint(456)

Type inference

  • 明示的に型を指定しなかった場合は、右辺の値から型推論される。
  • 数値の場合は、必要な精度により、適切な方が自動で選択される。
var i = 1 // int
j := 1 // int

a := 42           // int
b := 3.142        // float64
c := 0.867 + 0.5i // complex128

Constants

  • 定数には文字列、ブーリアン、数値を使うことができる。
  • :=記法は使えない
const Pi float64 = 3.14
const (
  A int = 123
  B string = "hello"
)

Numeric Constants

  • 数値定数は untyped(型を持たない)にできる
  • untyped な数値定数は高精度である
  • untyped な数値定数は、実行時の文脈で型が決まる
// タイプを指定していない
const Big = 1 << 100

func needFloat(x float64) float64 { return x * 0.1 }

func main() {
  fmt.Println(needFloat(Big)) // ここで初めて型が決まる(float64)
}

Flow control statements

for

  • 3 つの要素を記載できる
    • init statement:初回のみ実行。ここで定義した変数は for の中でのみ有効。
    • condition expression:毎回繰り返しの冒頭で評価
    • post statement:毎回繰り返しの最後で実行
  • ()は不要
for i := 0; i < 10; i++ { }

// 一部を省略できる
for ; sum < 1000; i++ { }

// init statementとpost statementを両方省略するときは
// セミコロンを省略できる
for sum < 1000 { }

// 何も要素を記載しなければwhileとして使える
for { }

if

  • ()は不要
  • init statement を記載することができる。ここで定義した変数は if の中でのみ有効。
if x < 0 {

} else {

}

if x := 100; x < 0 {
  // 実行されない
} else {
  // 実行される
}

switch

  • init statement を記載することができる。ここで定義した変数は switch の中でのみ有効。
  • 上から順に評価され、最初に当てはまった1つのケースだけ実行される。breakは不要。
switch a := 1; a {
case 1:
  fmt.Println("number is one")
case 2:
  fmt.Println("number is two")
}

条件式を省略することで、if-elseif-elseを簡略に書くことができる

switch a := 1; {
case a == 1:
  fmt.Println("number is one")
case a == 2:
  fmt.Println("number is two")
default:
  fmt.Println("number is unknown")
}

b := 1
switch {
case b == 1:
  fmt.Println("number is one")
case b == 2:
  fmt.Println("number is two")
}

defer

引数の評価のみ即時に行い、自身を包含するファンクションがreturnするまで待ってから実行する。

func main() {
  defer fmt.Println("world")
  fmt.Println("hello")
} // => "hello world"

defer statement はスタックに追加されていくため、最終的には逆順で実行される。

for i := 0; i < 10; i++ {
  defer fmt.Println(i) // 9 8 7 6 5 4 3 2 1 0
}

More types

Pointers

ポインタ=「変数の内容が存在するメモリ上のアドレス」を保持する変数

*タイプでポインタを宣言できる

var pointer *int

&変数でメモリアドレスを取得できる

i := 123
pointer = &i

*ポインタで「そのメモリアドレスに存在する内容」を参照できる。これを、dereferencing(値参照) や indirecting(遠回し?)という。

fmt.Println(*pointer) // => 123
*pointer = 456
fmt.Println(*pointer) // => 456

Structs

structは、複数フィールドから作成される型である。

type Car struct {
  Name string
  Age  int
}
car := Car{"bmw", 15}
fmt.Println(car) // => {bmw 15}

struct の値にはドットでアクセスできる。

car.Name = "nissan"
fmt.Println(car) // => {nissan 15}

Pointers to structs

  • struct のポインタで値参照する場合は、単純にpointer.Nameのようにすればできる。
  • *(pointer).Nameなどとする必要はない
car := Car{"bmw", 15}
pointer := &car
fmt.Println(pointer.Name) // => {bmw 15}

Struct Literals

全てのフィールド値を順番に記述する方法

car := Car{"bmw", 15}

name: value構文を使って、一部の値だけを記述する方法(指定しなかったフィールドには zero values が設定される)

car := Car{Name: "bmw"}  // Age:0 implicitly
car := Car{}      // Name:"" and Age:0 implicitly

&を頭につけると、作成された struct へのポインタを取得できる。

p := &Car{} // has type *Car

Arrays

[n]Tで、長さ n で型が T の配列を作成できる。配列の長さも型の定義に含まれる。長さは後から変更できない。

var a [3]int
a[0] = 100
a[1] = 200
a[2] = 300

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

Array 型の変数は、配列全体を表現している。C 言語のように配列の先頭を指すポインタではない。このため、代入を行うと配列全体がコピーされる。

a := [3]int{1, 2, 3}
b := a
b[0] = 999

fmt.Printf("%v", a) // [1 2 3]
fmt.Printf("%v", b) // [999 2 3]

Slices

詳細はこのブログを参照

  • スライスは Array の上に構築されている。Go では配列よりもスライスをよく使う。
  • []Tで型が T のスライスを宣言できる
  • スタートを前にずらすことはできない。後ろにずらすことはできる。
  • エンドは、前後にずらすことができる。
  • someArray[1:4]で、要素 1 から要素 3 までを含むスライスを作成できる
array := [6]int{1, 2, 3, 4, 5, 6}
slice := array[1:4]

fmt.Println(slice) // => 2,3,4

Slices are like references

  • スライスは配列への参照のようなもの
  • スライスはどんなデータも格納しておらず、単に元の配列の一部分を指し示しているだけ
  • スライスの要素を変更すると、その元となる配列の対応する要素が変更される
  • 同じ元となる配列を共有しているスライスは、お互いの変更が反映される
array := [6]int{1, 2, 3, 4, 5, 6}

slice1 := array[0:3]
slice2 := array[0:3]

slice1[0] = 123

fmt.Println(array)  // => 123, 2, 3, 4, 5, 6
fmt.Println(slice1) // => 123, 2, 3
fmt.Println(slice2) // => 123, 2, 3

Creating a slice with literals

  • いきなりスライスを作成するリテラル
  • 長さを指定しない配列リテラルのような書き方をする
  • 下記のスライスは、上段の配列と全く同じものを作成した上で、その配列を参照するスライスを返す。
// array literal
[3]bool{true, false, false}

// slice literal
[]bool{true, false, false}

Creating a slice with make

  • make(スライス, length, capacity)関数を使ってスライスを作成することで、動的にサイズ変更できる配列を取得できる。
  • 配列の各要素は Zero Values で埋められる
a := make([]int, 5) // [0:5]
// len=5 cap=5 [0 0 0 0 0]

b := make([]int, 1, 5) // [0:1]
// len=1 cap=5 [0]

c := b[:2] // [0:2]
// len=2 cap=5 [0 0]

d := c[2:5] // [2:5]
// len=3 cap=3 [0 0 0]

Slice default

スライスを作るときの上限・下限の値は省略できる

// 長さが10のArrayがあったとすると、下記はどれも等価
a[0:10]
a[:10]
a[0:]
a[:]

Slice length and capacity

  • スライスの上限・下限はあとから変更できる
  • length = スライスが持つ要素の数。len(s)で取得する。
  • capacity = スライスの最初の要素から数えた、元配列の要素数。cap(s)で取得する。
s := []int{2, 3, 5, 7, 11, 13}
// len=6 cap=6 [2 3 5 7 11 13]

s = s[:0]
// len=0 cap=6 []

s = s[:4]
// len=4 cap=6 [2 3 5 7]

s = s[2:]
// len=2 cap=4 [5 7]

スライスのスタートを後から遡って変更することはできない。一方、エンドポイントは cap の値まで拡張できる。

a := []int{1, 2, 3, 4, 5}

b := a[2:4]
fmt.Printf("%v", b) // [3 4]

c := b[0:cap(b)]
fmt.Printf("%v", c) // [3 4 5]

Nil slices

  • スライスの Zero Values はnilである
  • Nil slice は length と capacity が 0 で、元となる配列を持たない
var s []int // s==nil len(s)==0 cap(s)==0

Slices of slices

スライスはどんな型の値でも格納できる。例えば、スライスでも。

board := [][]string{
  []string{"_", "_", "_"},
  []string{"_", "_", "_"},
  []string{"_", "_", "_"},
}
board[0][0] = "X"

appending to a slice

  • append()を使うことで、簡単にスライス(配列)に要素を追加できる
  • スライスの背後にある配列の大きさが足りなくなったとき
    • 新しい配列が作成される。このとき、余分に配列の長さが確保される。
    • 古い配列の内容が新しい配列にコピーされる
    • 新しいスライスは新しい配列を指す
s := []int{1,2,3,4} // cap == 4
s = append(s, 5) // cap == 8
s = append(s, 6) // cap == 8
s = append(s, 7) // cap == 8
s = append(s, 8) // cap == 8
s = append(s, 9) // cap == 16

Range

Array や Slice はrange()でループ処理できる。

for index, value := range my_slice {}
// 省略するときは`_`にするか、削除する
for index := range slice {}
for _, value := range slice {}

Map

  • key-value ペア。
  • make()を使うことでマップを作成することができる。
  • map[T]Tで宣言する

make()でつくる

マップは宣言だけでは使えるようにはならない。必ず make で初期化をしてから使う必要がある。

var my_map1 map[string]int
my_map1 = make(map[string]int)

my_map1["hello"] = 123
fmt.Println(my_map1["hello"]) // 123

リテラルでつくる

リテラルを使うと、宣言、初期化、代入を一気に行うことができる。

my_map2 := map[string]int{
  "john": 33,
  "jim":  25,
}
fmt.Println(my_map2["jim"])   // 25

struct を値にする

cars := map[string]Car{
  "bmw": Car{
    "sedan", 2003,
  },
  "hino": Car{
    "truck", 1985,
  },
}

// 型名は省略もできる
cars := map[string]Car{
  "bmw": {
    "sedan", 2003,
  },
  "hino": {
    "truck", 1985,
  },
}

要素の削除

delete(my_map, key)

要素の取り出し

  • okは、要素が存在すればtrue、なければfalseになる
  • elemは、要素が存在すればその要素、なければ Zero Value が入る
elem, ok := my_map[key]

Function Values

関数は値でもある。変数に代入したり、引数や返値として使うこともできる。

func compute(fn func(string)) {
  fn("hello")
}

func main() {
  callback := func(message string) {
    fmt.Println(message)
  }
  compute(callback)
}

Function Closures

JavaScript のクロージャと同じようなことができる。

func adder() func(int) int {
  sum := 0
  return func(x int) int {
    sum += x
    return sum
  }
}

func main() {
  pos, neg := adder(), adder()
  fmt.Println(pos(1), neg(-1)) // 1 -1
  fmt.Println(pos(1), neg(-1)) // 2 -2
  fmt.Println(pos(1), neg(-1)) // 3 -3
}

Methods and interfaces

Methods

  • Go ではクラスがない代わりに、型に対してメソッドを定義することができる
  • メソッドは、「レシーバ」を引数にとる関数を使って定義する
  • レシーバは、funcと関数名の間に記載する

struct をレシーバで受け取る

type Car struct {
	Brand string
	Model string
}

// `(c Car)` がレシーバ
func (c Car) SayHello() string {
	return "My name is " + c.Brand + " " + c.Model
}

func main() {
	car := Car{"bmw", "3series"}
	fmt.Println(car.SayHello())
}

メソッドは単なる関数であることを忘れずに。上記コードは下記と等価である。

func SayHello(c Car) string {
	return "My name is " + c.Brand + " " + c.Model
}

func main() {
	car := Car{"bmw", "3series"}
	fmt.Println(SayHello(car))
}

struct 以外 をレシーバで受け取る

  • struct 以外の型にもメソッドを追加できる。
  • ただし、型が同一のパッケージ内で定義されている場合に限る
  • built-in types(intなど)に直接メソッドを追加することはできない。あくまでカスタムの型を定義し、その型にメソッドをアタッチすること。
type MyInt int

func (f MyInt) Abs() int {
  if f < 0 {
    return int(-f)
  }
  return int(f)
}

func main() {
  f := MyInt(-123)
  fmt.Println(f.Abs()) // => 123
}

Value Receiver と Pointer Receiver

前述のレシーバは Value Receiver と呼ばれ、値渡しである。メソッドを使って呼び出し元の変数等の値を変更したい場合は、Pointer Receiver を使う。

ポインタレシーバを使う理由は以下の 2 つ

  • 呼び出し元の変数の値を変更したいとき
  • 呼び出しのたびに変数がコピーされることによるオーバーヘッドを減らしたい時

一般的に、メソッドはレシーバの値を変更するために作られる場合が多いので、Pointer Receiver は Value Receiver よりも、よく使われる。

レシーバによる自動変換

レシーバがポインタを受け取る場合に、呼び出し側で「値」を渡した(&fのようにしなかった)としても、自動的にポインタに変換される。

type MyInt int

// `*T`とすることで、呼び出し元の変数をポインタとして受け取ることができる
func (f *MyInt) Abs() {
	if *f < 0 {
		*f = -(*f)
		return
	}
	return
}

func main() {
	f := MyInt(-123)
	f.Abs()
	fmt.Println(f) // => 123
}

逆に、レシーバが値を受け取る場合に、呼び出し側でポインタを渡したとしても、自動的に値に変換される。

func (f MyInt) Abs() {
	fmt.Println(f) // 2. 値で受け取っている
}

func main() {
	p := &MyInt(123)
	fmt.Println(p) // 1.ポインタで渡しても
}