混乱してくるので業務中に書いた、golangのメモ
データ構造
値と処理
|名前|役割|
|---|---|---|
|変数 |単数の値の型の入れ物|
|関数|単数の処理の型の入れ物|
|構造体|複数の値の型の入れ物|
|インターフェイス|複数の処理の型の入れ物|
複合体
名前 | 役割 | ゼロ値 |
---|---|---|
配列 | 固定長の配列 | 各要素のゼロ値 |
構造体 | 固定長の連想配列 | 各要素のゼロ値 |
スライス | 可変長の配列 | nil |
マップ | 可変長の連想配列 | nil |
可変長の複合体の注意
// スライスやマップは内部にlen、capの情報を独自に保持するため、
// nil(参照先のオブジェクトなし)でlen関数を行っても、実体を見ずに0が返ってくる感じ
// 変だと思っていたが、スライスなどのフィールドの値がゼロ化されただけの正常な感じと解釈している
v := map[string]string{}
fmt.Println(len(v), v==nil) // 0, false
var v2 map[string]string
fmt.Println(len(v2), v2==nil) // 0, true
スライスの構造
- arrayへの参照
- そのarrayのlen
- そのarrayのcap
マップの構造
TODO
インターフェイスの構造
TODO
リテラル
リテラル一覧
リテラルの種別 | 特徴 |
---|---|
基本リテラル | 初期型を持ち、柔軟に代入可能 |
複合リテラル | 構造体、配列、スライス、マップのどれかの値を作成する |
関数リテラル | クロージャーのこと |
型リテラル | 構造体、配列、ポインター、関数、インターフェイス、スライス、マップ、チャンネルの型のこと |
基本リテラル
リテラルの種別 | 特徴 |
---|---|
数値リテラル | 数値リテラル(10, 8, 16進数) |
浮動小数点リテラル | 浮動小数点 |
虚数リテラル | 虚数 |
ルーンリテラル | ユニコードのコードポイントの数値 |
文字列リテラル | (エスケープシーケンスの)解釈有りと無し版がある |
12 // リテラルは型を持たない(そのかわり初期型がある)ため、
// 12の場合は、intにもfloat64にも代入可能
var a int = 12
var g float64 = 12
複合リテラル
// 複合リテラルは構造体、配列、スライス、マップのどれかの値を作る
// また、構造体、配列、スライス、マップはそのままリテラルにできる
// 構造体リテラル、配列リテラル、スライスリテラル、マップリテラルと呼ばれる
struct{}{}, [...]int{}{}, []int{}{}, map[string]string{}
// それらの省略記法
[...][]int{[]int{1, 2}, []int{2, 3}}}
[...][]int{{1, 2}, {2, 3}}} // 省略記法
// また、複合リテラルの波括弧は配列などと同じ複数のインデックス式を意味するので
v := struct{
name string
age int
} {
name: "hoge", // 構造体の複合リテラルにはカンマが必要
age: 12,
}
// 複合リテラルの例
type MyInt int // 名前付き型(基底型は事前宣言済み型)
v := []MyInt{12} // 名前付き型MyIntの複合リテラル(スライスの値)
v2 := map[interface{}]interface{}{"age":12} // インターフェイスの型リテラルを持つ複合リテラル(マップの値)
// 複合リテラルはまた、アドレス演算子をつけることで、
// リテラル値と初期化されたユニークな変数へのポインタを生成する
v := &struct{}{}
// 関数型は間違えやすいので注意
type F func() // 関数リテラルを元に、名前付きの型(基底型は関数型)の宣言
// F{} // Error: 名前付きの型から関数リテラルは無理(これは複合リテラルと評価されるため)
[]F{func(){}} // 複合リテラルの値(構造体、配列、スライス、マップのどれか)として使用しなければならない
関数リテラル(クロージャー)
// 関数リテラルは複合リテラルではない点に注意
// 複合リテラルは波括弧に複数要素を意味し、インデックス式を使うが、
// 関数リテラルの波括弧はスコープを意味する
func()int{return 1} // クロージャーだからスコープのセマンティクスの波括弧を使ったリテラルが許される
型リテラル
// 構造体、配列、ポインター、関数、インターフェイス、スライス、マップ、チャンネルの型のこと
interface{} // Any型など
struct{}
// ※型リテラルと複合リテラルの違いは以下のような感じ
struct{} // 型リテラル
struct{}{} // 複合リテラル
型
事前宣言済み型(predeclared type)
// プリミティブな事前宣言済み型は以下
bool byte complex64 complex128 error float32 float64
int int8 int16 int32 int64 rune string
uint uint8 uint16 uint32 uint64 uintptr
名前付き型(named types)
// 名前付き型は次の2種類
// ①事前宣言済み型か、
// ②typeで宣言された型(型リテラルに名前をつけるか、既存型にエイリアスをつけられる)
type 新しい型名 型リテラル or 既存の型
無名型(unnamed types)
// 無名型は名前付き型以外の型、
// 要はtype宣言されていない、型リテラルのこと
[]string
*int
複合型(composite type)
// 以下が複合型
Composite types —array, struct, pointer,
function, interface, slice, map, and channel types—
// つまり、type宣言されていない複合型=無名型であり、
// 構造体、配列、スライス、マップか、型リテラルを使ってtype宣言した名前付き型を
// 波括弧とインデックス式と共にインスタンス化したものが、複合リテラルである(ややこしい)
基底型(underlying type)
// 全ての型Tには基底型がある。
// Tがboolean, numeric, or string types, or a type literalの場合はTの基底型はそれ自身
// 型宣言(type)で作成した型は元の型の基底型を引き継ぐ
type T1 string //T1の基礎型はstring
type T2 T1 //T2の基礎型はT1の基礎型のstring
type T3 []T1 //T3の基礎型は[]T1(なぜなら[]T1は型リテラルのため)
type T4 T3 //T4の基礎型はT3の基礎型の(なぜなら[]T1は型リテラルのため)
動的型(dynamic type)、静的な型(static type)
var n int = 42 // nの静的型はint
var s string = "hello" // sの静的型はstring
var v interface{} // vの静的型はinterface{}
v = n // vの静的な型はinterface{}、動的型はint
キャスト(型関数)、型アサーション(型無名メソッド)
// キャスト
type (MyInt1 int, MyInt2 int)
var m1 MyInt1 = 10; var m2 MyInt2
m2 = MyInt2(m1) // 基底型が一致しているので変換可能
// アサーション
v = x.(T); v, ok = x.(T)
switch t := x.(type) { // switch文ではtypeキーワードを使える
case T:
}
初期化と初期化子と初期型と初期値(ゼロ値)
// newやmakeは初期化子
var hoge T // 明確な初期化がないので、ゼロ値が入る
hoge2 := new(T) // Tの参照型(ポインタ型)
hoge3 := make([]T, length, capacity) // Tの値型
ポインタ
ポインタ型の型リテラル
*int // * と 型リテラル
アドレス演算子(Address Operator)
// &"" // 単一リテラルにはメモリが割り当てられていないので無理
v := &struct{}{} // 複合リテラルはOK
デリファレンス(関節演算子)
var n int = 1
var p *int = &n
fmt.Println(n) // 1
fmt.Println(&n) // 0x10328000
fmt.Println(p) // 0x10328000
fmt.Println(*p) // 1 デリファレンス
変数
A variable is a storage location for holding a value.
The set of permissible values is determined by the variable's type.
変数宣言は4種類
var hoge T // 型のゼロ値で初期化される
var hoge T = 12 // hogeの型はTで12がその初期値
var hoge = 12 // 型なし変数宣言、リテラルの型または初期型がhogeに使用される
hoge := 12 // ローカル変数の宣言
所有権と操作権
var hoge string
hoge = "age" // hogeはインスタンスの所有権と操作権をもってる
var hoge2 string = "age"
hoge3 := "age"
ローカル変数、グローバル変数
var glo = 12 // トップレベルスコープでの宣言はグローバル変数となる
func main(){
hoge := 12 // ち◯こ演算子はローカル変数のみ使える
}
定数
ビルドインの定数
true, false, iota
作成方法
// 定数に使用できるのは基礎型のみ
const hoge int = 12 // 型ありの定数
const hoge2 = 12 // 型無の定数、なのでリテラルと同じように扱える
// var hoge3 float = hoge // エラー
var hoge3 float = hoge2 // OK
列挙体
const (
c1 int = iota // 0
c2 // c2==1, int型
_ // iotoと解釈される
c3 // c3==3, int型
)
スコープ
スコープの種類
universe block
package block
file block
"if", "for", and "switch" statement
Each clause in a "switch" or "select" statement
ブロックのスコープ記号
{
// here is local scope
}
ローカルスコープ、グローバルスコープ
var global int
func hoge() {var local int}
レキシカルスコープ
TODO
関数
ビルドイン関数
append cap close complex copy delete imag len
make new panic print println real recover
仮引数(Parameter)と実引数(Argument)
func hoge(aaa string) {} // aaaはhoge関数の仮引数
aaa := "age"
hoge(aaa) // aaaはhoge関数の実引数
名前付き戻り値とブランク、型省略
func hoge(age int, _ string, name, addr string) {}
可変長引数(レスト引数)
// 可変引数は最後の引数で指定できる
func hoge(age int, opt ...string) {}
// 実引数を入れる方法は2つ
hoge(12, "opt1", "opt2") // string型を可変で実引数にわり当てられる
hoge(12, []string{"opt1", "opt2"}...) // slice型を予め実引数にすることも可能
戻り値
// 戻り値を返す方法は2つ、
func hoge() string {return ""}
func hoge() (string, error) {return "", nil } // 複数戻り値がある場合はタプルで指定する
func hoge() (name string) {return} // nameはゼロ値で初期化されるので、それが返る
func hoge() (name string) { name="aho"; return } // 名前付き戻り値バージョン、"aho"が変える
func hoge() (name string) { return "teko"} // return文に値があった場合はそちらが優先される
関数型
type F func() // 関数の型リテラルを名前付きの型として定義
s := []F{func(){}} // ここで []F{F{}}とはできない
関数リテラル(クロージャー)
f := func() {}
即時関数(クロージャー)
func(i int) int { return i }(0)
代入
代入とブランク
v, _ = "hoge", "hoge"
_ = v // unused variable回避
タプル?
f := func()(v, v2 string){} // 複数返せる関数を作れる
v, v2 = f() // 複数代入できる
## ゼロ値(zero value)
```go
// 変数宣言またはnew関数のコールか、複合リテラルまたはmake関数のコールか、
// 明確じゃない初期化が提供された時
false for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps
構造体
構造体の型リテラル
stuct{}
構造体リテラル
//変数(フィールド)ごとに値を格納するメモリを持つ値型のデータ構造
// 型リテラル表記のときの波括弧はカンマで区切られた式を持つ複数の要素を示しており、
// 構造体リテラルでもカンマが必要な理由は、それを統一するため?
stuct{}{}
フィールド
type T {
age int
}
v := &T{age: 12}
v.age // 12
匿名フィールド
// 匿名フィールドに設定できるのは、名前付き型もしくはそのポインタ型のみ(型リテラルだめ)
// また、匿名といってもその名前付き型の名前が暗黙的に型名になる
// 匿名フィールド内のフィールドとメソッドは、その構造体直管のフィールドとメソッドへ昇格される
type S []string
type T struct {
string
// []string // NG
S // OK
}
タグ
// タグはリフレクションで解析可能
配列
配列の型リテラル
[2]int
[10]int
[...]int
配列リテラル
[2]int{1,2}
[10]int{1,2}
[...]int{1,2,3}
配列の操作
a := make([2]int, 2, 10) // 2=length, 10=capacity, 各要素はゼロ化されている
a[0] = 1
fmt.Println(a[0])
b := a // arrayは値型なので=でコピー可能
スライス(可変長配列)
スライスの型リテラル
[]string
[]interface{}
[]int
スライスリテラル
[]string{"1","2"}
[]interface{}{1,""}
[]int{1,2,3}
スライスの操作
v := make([]int, 2, 10) // 2=length, 10=capacity, 各要素はゼロ化されている
len(v) // 長さ
cap(v) // 容量
// 結合
c = append(a, b...) // cはaとbが結合されたもの
// append
// ※appendは元のスライスの容量を超えた場合、新しいスライスを作成する
a = append(a, b, c) // b and c are element
// copy
b = make([]T, len(a))
copy(b, a) // sliceは参照型なので=ではコピーはできない
// delete
a = append(a[:i], a[i+1:]...)
// cut
a = append(a[:i], a[j:]...)
// pop
x, a = a[len(a)-1], a[:len(a)-1]
// push
a = append(a, x)
// unshift
a = append([]T{x}, a...)
// shift
x, a := a[0], a[1:]
// 空にする
a := [...]int{1, 2, 3, 4, 5}
a = nil
fmt.Println(a) // => "[]"
スライス式
// スライス式はつぎの書式 [low:hight:max]
s[0:1] // 0は開始値, 1は終了値
s[0:] // 終了値はlen(slice)と同じ値になる
s[:] // 開始値は0, 終了値はlen(slice)と同じ値になる
// ※(フル)スライス式で取得したスライスは取り出し元と同じメモリを使用されるので注意
s := []int{1, 2, 3}
s2 := s[:]
s2[0] = 12 // 12, 2, 3 となっている
フルスライス式
// スライス式は暗黙的にmax=len(slice)となっている。
// そのため、明示的にフルスライス式でmaxを指定しないと、
// 切り出したものより大きな値にアクセスできてしまう
a := make([]int, 10) // 長さ10の配列
s := a[0:5] // 長さ5のスライスsを切り出す
t := s[0:10] // sからsより長いスライスtを切り出す
文字列
len("あ")
"あ"[0:4] // あ(3バイト)
マップ
マップの型リテラル
map[string]int
マップリテラル
map[string]int{"key": 1, "key2": 2}
マップ操作
m := make(map[string]int) // map作成
v := map["key"] // キーの要素または、存在しないキーの場合はゼロ値が返る
v, hasKey := map["key"] // 上記の方法だと要素が存在するか分からないので
// 第二引数を見る(hasKeyはbool型)
// キーの削除
delete(m, "key") // mからkeyの要素を削除する
クラスっぽく
フィールド
type T struct {f string}
メソッド
type T struct {f string}
func (*T) M() {}
アクセス権限
type T struct{
pri int // プライベート
Pub int // パブリック
}
アクセッサー
func (*T) GetHoge(hoge string) string{return T.hoge} // ゲッター
func (*T) SetHoge(hoge string) {T.hoge = hoge} // セッター
コンストラクタとデストラクタ
type T struct{ hoge string }
func NewT(hoge string) *T{
return &T{ hoge: hoge } // ここはイニシャライザ
}
インデクサ?
type P map[string]interface{}
func (p *P) hello() { fmt.Println((*p)["age"]) }
p := &P{}
(*p)["age"] = "hello"
p.hello()
インプリメンツ?(interface)
type H interface {
hello()
}
type T struct{ H }
func (r *T) hello() { fmt.Println("hello desu") }
var v H = &T{}
v.hello()
エクステンズ?(is-a)
type T struct{ f string }
func (r *T) hello() {}
type T2 struct{ T }
func (r *T2) hello2() {}
v := &T2{}
v.hello()
v.hello2()
アグリゲート?(has-a)
type T struct{ f string }
func (r *T) hello() {}
type T2 struct{ t: T }
func (r *T2) hello2() {}
v := &T2{t:&T{}}
v.t.hello()
v.hello2()
デリゲート (構造体にインターフェイスの埋め込み)
type Superable interface{ a(); b() }
type Super {}
func (*Super) a() {}
func (*Super) b() {}
type Subable interface{ a(); b(); c() }
type Sub { Superable }
func (*Sub) c() {}
// main
var sub Subable := &Sub{Superable: &Super{}}
sub.a(); sub.b(); // subはaとbの処理をsuperに委譲している
sub.c()
アップキャスト・ダウンキャスト
TODO
オーバーライド?
type T struct{ f string}
func (r *T) hello() { fmt.Println("hello") }
type T2 struct{ T }
func (r *T2) hello() { fmt.Println("hello from T2") }
v := &T2{}
v.T.hello() // もとの
v.hello() // オーバーライド済み?
オーバーロード?
type T struct{ f string}
func (r *T) hello() { fmt.Println("hello") }
type T9 struct{ T}
func (r *T9) hello(args ...interface{}) { // ここで型スイッチを書いて、r.T.hello()を呼び出す感じ? }
v9 := &T9{}
v9.hello("age", false) // オーバーロードっぽく
クラスメソッド?
type T struct{}
func (*T) hello() {}
(*T).hello(nil) // メソッド式では第0仮引数がそのレシーバーになる
インターフェイス
Any型
type Any interface{}
var v Any = "なんでも"
ダックタイピング
// 明示的に実装する必要なし
type I interface{ sound() }
type T struct{}
func (*T) sound() {}
var v I = &T{}
v.sound()
ジェネリクス?
// TODO
v interface{} = 12
vi, ok := v.(int)
型アサーション
v interface{} = 12
vi, ok := v.(int)
型スイッチ
var v interface{} = 12
switch v.(type) { // vの静的型がインターフェイス型だからできる
case int: // 値ではなく、型で分岐する
fmt.Println("基底型はint")
case nil: // nil(値)が使える(jsでいうundefined)
fmt.Println("基底型は不明") // これは値が無くて型が分からないときのため
}
メソッド
レシーバーと宣言方法
type T
func (r *T) Method() {}
メソッドの呼び出し方
type T
func (*T) Method() {}
v := &T{}
v.Method() // メソッドのインスタンスを含めて使いたい時
var v2 *T
v2.Method() // メソッドを単純に使いたいときだけ
メソッド式 (method expression)
func (p T) M(arg1 string) {} // こんな形で書くが、
func T.M(p T, arg1 string) {} // 内部的にはこんな形になっている
T.M(T{}, "hoge) // なので左の用に呼び出せる
メソッド式の呼び出し
type T struct{ Name string }
func (r T) M1() {}
func (*r T) M2() {}
p := P{Name: "Hoge"}; p2 := &P{Name: "Hoge"}
// 以下は等価
p.M1(); P.M1(p) // メソッド式
p2.M1();(*P).M2(p2) // メソッド式 ( p2.M1() は暗黙的に型変換が行われている)
ポインタ型レシーバーと値型レシーバー
// レシーバーは第0引数であり、もし値型でレシーバーを書いた場合は、そのレシーバーの値がメソッドにコピーされるので、そのレシーバー(第0引数)を更新することができない
// つまり、値型レシーバの値はメソッド内で書き換えても元のレシーバの値にはまったく影響がない
type T struct{ name string }
func (r T) getName() { return r.Name }
func (r T) setName(name string) { r.name = name}
v := T{name: "Taro"} // vの値はsetNameメソッドに値型としてコピーされるので、フィールドを書き換えても影響ない
T.setName(v, "Hoge")
v.setName("Hoge")
fmt.Println(v.getName()) // Taro
エラー処理
defer構文
// deferは順次にスタックのように積み上げられ、関数実行後に実行される。
// 名前付き戻り値と引数はdeferでも参照可能
defer func(){}() // 関数終了後2番目に実行される
defer func(){}() // 関数終了後1番目に実行される
パッケージ
アクセス権限
var pri int // プライベート
var Pub() {} // パブリック
パッケージ名ルール
package main // mainを除くパッケージはフォルダ名と同一になる
トップレベルスコープのキーワード
// パッケージのトップレベルは以下の5つ(単数の値、単数の処理 or タイプ)
type, func, method, var, const
パッケージの初期化関数
func init() {}
func init() {} // init関数は同じパッケージに複数かける
インターナルパッケージ
// internalが含まれる階層配下はインターナルパッケージ
package internal
インポートのパス
パスの指定方法 | 意味 |
---|---|
相対パス | 同一パッケージ |
絶対パス | なし |
その他のパス | golangのパッケージ |
インポートの方法
パスの前方の識別子 | 意味 |
---|---|
なし | 普通のインポート |
エイリアス | パッケージ名のエイリアス |
ドット(.) | パッケージ名省略してそのパッケージの中のものを使用可能 |
ブランク(_) | インポートのみ(そのパッケージの中のinit関数は実行される) |
コンパイルの対象外名
名前 | 効果 |
---|---|
ドット(.)かブランク(_)で始まるファイル | コンパイル対象外 |
testdataフォルダ | テストデータ格納用フォルダ。コンパイル対象外 |
構文
順次構造
var hoge = 12
hoge = 14
fmt.Println(hoge)
分岐構造
// if文
if v := true; v == true { // ち◯こ演算子でローカル変数の宣言も可能
fmt.Println("true")
}
// select文
select hoge { // fallthroughで抜けれるかわりbreakはなし
case 1:
fmt.Println("hoge = 1")
default
}
// switch文
反復構造
// while文
i := 0
for ; i < 10; { i++ }
// while true {}
for {}
// for文
for i:=0;i<10;i++ {} // continue, breakなど使用可能
// 拡張for文(foreach ループ)
for i, v := range hoge {}
gotoモア
goto jump
fmt.Println("なぜ表示されない?")
jump: // ラベル
並列処理
gorutine(routine)
go func() {}()
共有変数
TODO
チャンネル
TODO
その他
ルーチンの外部化方法
- パッケージ(モジュール、ライブラリ、フレームワークとか?)
メインルーチンの共有方法
- 構造型プログラミングっぽく(メインルーチンで型ごとの条件分岐を書く)
- インターフェイス
- 構造体の埋め込み
- ジェネリクスっぽく
サブルーチンの共有方法
- 関数
- プロシージャ
- メソッド
- クロージャー
ルーチン独立化
- 仮引数・実引数
- ローカル変数(グローバル変数を使わない)
ルーチン隠蔽化(カプセル化)
- アクセッサー
- インターナルパッケージ
- スコープを切る
- アクセス権限
- フィールド
- クロージャー
参考文献
https://golang.org/ref/spec
https://golang.org/doc/effective_go.html
https://github.com/golang/go/wiki/SliceTricks
http://e-words.jp/w/%E3%83%AB%E3%83%BC%E3%83%81%E3%83%B3.html
http://www2.cc.niigata-u.ac.jp/~takeuchi/tbasic/Intro2Basic/Structure.html#control
http://qiita.com/atsaki/items/3554f5a0609c59a3e10d
https://ja.wikipedia.org/wiki/%E5%A7%94%E8%AD%B2
http://qiita.com/atsaki/items/3554f5a0609c59a3e10d#%E5%9E%8B%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9
http://stackoverflow.com/questions/32983546/named-and-unnamed-types
http://sixeight.hatenablog.com/entry/2015/03/16/032222
https://skatsuta.github.io/2015/12/29/value-receiver-pointer-receiver/
http://golang.jp/tag/go%E8%A8%80%E8%AA%9E%E4%BB%95%E6%A7%98/page/3
http://qiita.com/jca02266/items/56a4fb7b07b692a6bf34
http://qiita.com/ruiu/items/f3516b705e6a7f2efb05
https://blog.golang.org/go-slices-usage-and-internals