記法
MixedCaps or mixedCaps
戦闘が大文字の場合,パッケージ外にエクスポートされることを意味する
変数宣言
変数名 型 で宣言
j int = 1
varで複数の変数を宣言できる
var i, j int = 1, 2
暗黙の変数宣言
関数内でのみ :=
で暗黙的な変数の宣言が可能
func main() {
k := 3
}
型変換の明示
Goではint型とfloat型などでも明示的に変換しないとエラーをはく
暗黙の変数宣言の際は代入される値から推論
ループ
whileみたいに書きたいときはforの宣言とイテレータを書かないことで実現する
for sum < 1000 {
sum += sum
}
ループ条件を抜くことで無限ループを簡潔に記述することもできる
if
ifの条件式中に変数を書くことができる
変数はifやelse文の中でのみ有効
if v := math.Pow(x, n); v < lim {
return v
}
switch
該当するcaseのみを実行
caseは定数である必要がない
breakは自動的に入るので書く必要なし
defer
deferに渡した関数は呼び出し元の関数がreturnされたあとに実行される
複数ある場合はあとに渡したものから実行される
func main() {
defer fmt.Println("こちらがあと")
fmt.Println("こちらが先")
}
ポインタ
var p *int
i := 42
p = &i
fmt.Println(*p) // 42
配列
配列のサイズは変えることはできない
a[low:high]
でスライスできる(high-1までとれる)
※スライスはあくまでもとの配列への参照
以下のスライスは等価
a[0:10]
a[:10]
a[0:]
a[:]
len()
で長さ,cap()
で容量を取得できる
ここでいう容量とはスライスの最初の要素から数えてもととなる配列の要素数である
make関数を利用するうことで動的サイズの配列を作成できる
make([]type, length, capacity)
makeでつくった配列をスライスした場合,中身はゼロ値で埋められる
map
Rubyのハッシュ, Pythonの辞書てきなやつ
map[Type]Type // "":""のmap
closure(クロージャ)
クロージャは、それ自身の外部から変数を参照する関数値
関数を返す関数で初期化すると関数を返す関数内の変数の値を保持できる
使い所が難しいそう…
Methods
Goにはクラスの仕組みはないがmethodを定義できる
// 型に生やす場合
func (変数 生やしたい型) methodName(引数 型) 返却する型 {}
// 通常の関数
func methodName(引数 型) 返却する型 {}
※レシーバ(メソッドの呼び出される側→fmt.Printlnのfmt)を伴うメソッドの宣言はレシーバ型が同じパッケージに存在する必要がある
Methodには引数のコピーを渡すため,引数となる値そのものを変えたい場合はポインタレシーバにする必要がある
https://go-tour-jp.appspot.com/methods/4
以下のような時,vは変数だがポインタレシーバを保つ場合,v.Scale(2)
を(&v).Scale(2)
として解釈する
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(2)
fmt.Println(v) // &{6, 8}
}
逆にポインタを変数として解釈も可能
この場合、 p.Abs() は (*p).Abs() として解釈される
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
p := &Vertex{4, 3}
fmt.Println(p.Abs())
}
ポインタレシーバを使う理由
- レシーバが指す先の変数を変えたい
- メソッド呼び出しのたびの変数のコピーを避けるため
一般的には変数レシーバかポインタレシーバのどちらかで統一すべき
interface
メソッドのシグニチャの集まりで定義
メソッドを定義した変数をinterface型に持たせることができる
https://go-tour-jp.appspot.com/methods/9
interfaceの説明
https://qiita.com/tenntenn/items/eac962a49c56b2b15ee8
空のインターフェースは任意の型の値を保持できる.
var i interface{}
インターフェースの値愛が具体的な型を保持し, その方の値をt
ni
代入することを意味する
成功したかのbool値も取れる
もしfalseの場合, Typeのゼロ値が代入される
t := i.(Type)
t, ok := i.(Type) // ok = true, false
以下はStringerインターフェースを使ってfmt.Stringerインターフェースを実装している
つまりstringとして扱うことができる
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
エラー状態をerror
インターフェースで実装する
一般的にはerror変数がnilなら成功, それ以外ならErrorを出力と言った形をとる
Errorの中身を以下のように作っておくことも多い
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s",
e.When, e.What)
}
並列処理 goroutine
並列処理周り
select
がよくわからなかった
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)
}
Exercise
Map
func WordCount(s string) map[string]int {
wordCounter := make (map[string]int)
words := strings.Fields(s)
fmt.Println(words)
for i := 0; i < len(words); i++ {
_, ok := wordCounter[words[i]]
if ok {
wordCounter[words[i]] += 1
} else {
wordCounter[words[i]] = 1
}
}
return wordCounter
}
func main() {
wc.Test(WordCount)
}
Stringers
package main
import "fmt"
type IPAddr [4]byte
// TODO: Add a "String() string" method to IPAddr.
func (ip IPAddr) String() string {
return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3] )
}
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
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string {
return fmt.Sprintf("cannot Sqrt negative number: %g", float64(e))
}
func Sqrt(x float64) (float64, error) {
x64 := float64(x)
xn := float64(0)
n := 10
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
for ; xn * xn < 64; xn++ {
}
for i := 0; i < n; i++ {
xn = (xn + x64 / xn) / 2
}
return xn, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
Gpを正しく書くために
gofmt
ファイルフォーマッター
goimport
gofmtの上位互換. import周りを挿入削除してくれる
go vet
バグになりそうなコードの検出
golint
Goらしくないコードを検出
プロジェクト
プロジェクト名は 小文字
testdata/や_で始まるディレクトリはGoのパッケージとしてみなさない
ライブラリ
Web Application FrameWork
- Gin
- Echo
- Iris
- Goa
O/R マッパー
- Xorm
- GORM
- gorp
- beego orm
環境変数, 設定, key管理
- toml
APIの作り方