Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Go入門

22,183 views

Published on

Goに関する資料をまとめたものです。

Published in: Technology
  • Be the first to comment

Go入門

  1. 1. Go入門 ver. 2017/04 The Go gopher was designed by Renee French. The gopher stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license.
  2. 2. アジェンダ ■ 自己紹介 ■ Goの紹介 ■ Goの基本 ■ 型・メソッド・インタフェース ■ ゴールーチン・チャネル ■ ネットワークプログラミング ■ go test と testingパッケージ ■ ハンズオン 2
  3. 3. 自己紹介 上田拓也 twitter: @tenntenn ■ コミュニティ活動 Google Cloud Platform User Group (GCPUG) Tokyo Goビギナーズ golang.tokyo Go Conference 3
  4. 4. Goの紹介 ● Goとは? ● Goの特徴 ● Goを勉強するには? 4
  5. 5. Goとは? Googleが開発しているプログラミング言語 ■ 特徴 ● シングルバイナリ・クロスコンパイル ● 強力でシンプルな言語設計と文法 ● 並行プログラミング ● 豊富な標準ライブラリ群 ● 周辺ツールの充実 Goの紹介/Goとは? 5
  6. 6. Goの特徴 − シングルバイナリ・クロスコンパイル − ■ 環境変数のGOOSとGOARCHを指定する 開発環境とは違うOSやアーキテクチャ向けに クロスコンパイルできる Goの紹介/Goの特徴 − シングルバイナリ・クロスコンパイル − 6 # Windows(32ビット)向けにコンパイル $ GOOS=windows GOARCH=386 go build # Linux(64ビット)向けにコンパイル $ GOOS=linux GOARCH=amd64 go build シングルバイナリになるので 動作環境を用意しなくてよい go buildはコンパイルするコマンド
  7. 7. Goの特徴 − 強力でシンプルな言語設計と文法 − ■ スクリプト言語の書きやすさ ● 冗長な記述は必要ない ■ 型のある言語の厳密さ ● 曖昧な記述はできない ■ 考えられたシンプルさ ● 機能を増やすことで言語を拡張していくこと はしない Goの紹介/Goの特徴 − 強力でシンプルな言語設計と文法 − 7 Goに入ってはGoに従え = 言語の思想を理解しよう
  8. 8. Goの特徴 − 並行プログラミング − ■ ゴールーチン ● 軽量なスレッドに近いもの ● goキーワードをつけて関数呼び出し ■ チャネル ● ゴールーチン間のデータのやり取り ● 安全にデータをやり取りできる Goの紹介/Goの特徴 − 並行プログラミング − 8 チャネル ゴールーチン A ゴールーチン B データ データ go f()
  9. 9. Goの特徴 − 周辺ツールの充実 − ■ go tool として標準/準標準で提供 ■ サードパーティ製のツールも充実 ■ IDEによらない独立したツールとして提供 Goの紹介/Goの特徴 − 周辺ツールの充実 − 9 go build ビルドを行うコマンド go test xxxx_test.goに書かれたテスト コードの実行 go doc, godoc ドキュメント生成 gofmt, goimports コードフォーマッター golint コードチェッカー、リンター gocode コード補完
  10. 10. Goの特徴 − 豊富な標準ライブラリ − ■ 標準ライブラリ一覧  https://golang.org/pkg/ Goの紹介/Goの特徴 − 豊富な標準ライブラリ − 10 net/http HTTPサーバなど archive, compress zipやgzipなど crypto 暗号化 encoding JSON, XML, CSVなど html/template HTMLテンプレート os, path/filepath ファイル操作など
  11. 11. Goの勉強するには? ■ コミュニティ ● gophers-slack ○ 世界中のGopherが集まるチャット ● Qiita #Go ○ Go言語の初心者が見ると幸せになれる場所 ■ 書籍 ● The Go Programing Language(日本語) ● みんなのGo言語 Goの紹介/Goの勉強するには? 11
  12. 12. Goの基本 ● A Tour of Goをやろう ● for, if, switch ● Goのインストール ● GOPATH ● go tool 12
  13. 13. A Tour of Goをやろう ■ A Tour of Go ● Goのチュートリアル ● Web上で実行できる ● Basicsにチャレンジしてみよう Goの基本/A Tour of Goをやろう 13
  14. 14. 繰り返し:for ■ Goの繰り返しはforのみ Goの基本/繰り返しfor 14 // いつものfor for i := 0; i <= 100; i++ { } // while的な使い方 for i <= 100 { } // 無限ループ for { } ()はいらない
  15. 15. 分岐:if ■ 条件式の前に代入文などが書ける Goの基本/分岐:if 15 // いつものif if a == 0 { } // 代入文を書く if a := f(); a > 0 { fmt.Println(a) } else { fmt.Println(2*a) } ()はいらない
  16. 16. 分岐:switch ■ caseに式が書ける ■ breakは書かなくてよい Goの基本/分岐:switch 16 switch a { case 1: fmt.Println("a is 1") default: fmt.Println("default") } swtich { case a == 1: fmt.Println("a is 1") } caseをまたぐ際には、 fallthroughを使う 何もしないと breakになる
  17. 17. Goのインストール ■ インストール方法  Goの公式サイトからダウンロード ■ ソースコードからビルドする  Goの公式サイトを参考にする。 Goの基本/Goのインストール 17 Go1.5以上はビルドにGoが必要 そのため1.4のバイナリを入れておく
  18. 18. GOPATH ■ GOPATHとは?  Goのソースコードやビルドされたファイルが入るパス。 importされるパッケージもここから検索される。 Goの基本/GOPATH 18 $GOPATH ├── bin │ └── fuga ├── pkg │ └── darwin_amd64 │ └── hoge.a └── src ├── fuga │ └── main.go └── hoge └── hoge.go ビルドされた実行可能ファイルが入る ビルドされたパッケージが入る。 pkg/GOARCH/pkgname.a mainパッケージのGoソース。 src/cmdname/*.go 自作パッケージのGoソース。 src/pkgname/*.go
  19. 19. go tool − go install − ■ go install  ビルドして、GOPATH以下に配置するコマンド。 Goの基本/go tool − go install − 19 $ export GOPATH=`pwd` $ go install fuga $GOPATH ├── bin │ └── fuga ├── pkg │ └── darwin_amd64 │ └── hoge.a └── src ├── fuga │ └── main.go └── hoge └── hoge.go go install によって生成されたファイル 手元で試してみよう! (src/fugaとsrc/hogeだけを使用)
  20. 20. go tool − go get − ■ go get  パッケージをダウンロードしてビルドしてGOPATH以下に配置 するコマンド。 Goの基本/go tool − go install − 20 $ export GOPATH=`pwd` $ go get github.com/nsf/termbox-go └── src └── github.com ├── mattn │ └── go-runewidth │ ... │ └── runewidth_windows.go └── nsf └── termbox-go ... └── terminfo_builtin.go 依存するパッケージも インストールされる . ├── pkg │ └── darwin_amd64 │ └── github.com │ ├── mattn │ │ └── go-runewidth.a │ └── nsf │ └── termbox-go.a 手元で試してみよう!
  21. 21. 型・メソッド・インタフェース ● 型の種類 ● 配列・スライス・マップ・構造体 ● type ● メソッド・インタフェース ● 埋め込み 21
  22. 22. 型の種類 型・メソッド・インタフェース/型の種類 22 組み込み型 int, float64, string など 配列 [100]int など 要素の型と要素数は固定 スライス []int など 要素の型、要素数は可変。 マップ map[string]int など 連想配列。 構造体 struct { a int } など フィールドのリストを持つ インタフェース interface { m() int } など メソッドのリストを持つ
  23. 23. 配列 −1− [要素数]要素の型 型・メソッド・インタフェース/配列 −1− 23 var a [3]int a[1] = 10 b := [...]int{1, 2, 3} for i, n := range b { fmt.Println(i,"/",len(b),"=>", n) } Playgroundで動かす 要素数が違えば別の型 ...で初期値の要素に合わせる 添字と値で繰り返せる
  24. 24. 配列 −2− 値でコピーされる 型・メソッド・インタフェース/配列 −2− 24 a := [3]int{1, 2, 3} b := a a[0] = 10 for i := range a { fmt.Println(a[i], b[i]) } 参照がコピーされる わけではない 10, 1 2, 2 3, 3 関数に渡した場合も 同様に値がコピーされる Playgroundで動かす 2つ目は省略可
  25. 25. スライス −1− []要素の型 make([]要素の型, 要素数[, キャパシティ]) 型・メソッド・インタフェース/スライス −1− 25 a := make([]int, 3, 10) fmt.Println(a, len(a), cap(a)) b := []int{1, 2, 3} for i, n := range b { fmt.Println(i,"/",len(b),"=>", n) } Playgroundで動かす スライスでもrangeは使える
  26. 26. スライス −2− スライスの背後には配列がある 型・メソッド・インタフェース/スライス −2− 26 a := [...]int{1, 2, 3, 4} b := a[1:3] fmt.Println(b, len(b), cap(b)) Playgroundで動かす 1 2 3 4 a[0] a[1] a[2] a[3] b[0] b[1] len(b) = 2 cap(b) = 3配列 スライス
  27. 27. スライス −3− append(スライス, 要素...) スライス 型・メソッド・インタフェース/スライス −3− 27 var a []int // nil for i := 0; i <= 10; i++ { a = append(a, i * 10) fmt.Println(len(a), cap(a), a) } Playgroundで動かす appendした際にcapを 超えた場合は 新しく配列が確保される
  28. 28. スライス −4− ■ 課題1 配列とスライスをそれぞれ関数の引数や戻り値に した場合の挙動の違いを考えてみよう。 ■ 課題2 スライスへの任意位置への挿入、削除を実装して みよう。 型・メソッド・インタフェース/スライス −4− 28 // スライスaとbを結合 c := append(a, b...) Playgroundで動かす 可変長引数に スライスを展開
  29. 29. マップ map[キーの型]値の型 make(map[キーの型]値の型[, キャパシティ]) 型・メソッド・インタフェース/マップ 29 a := make(map[string]int) a["c"] = 100 n, ok := a["c"] fmt.Println(n, ok) b := map[string]int{"c":2, "d":4} for k, v := range b { fmt.Println(k, v) } Playgroundで動かす 値が存在すればnはその値、okはtrue 存在しなければ、nはゼロ値、okはfalse キーと値で繰り返せる
  30. 30. 構造体 フィールドのリストを持つデータ構造。 フィールドの型は任意の型を指定できる。 構造体のゼロ値は、フィールドすべてがゼロ値の構造体。 型・メソッド・インタフェース/構造体 30 a := struct{ N int s string }{ N: 100, s: "hoge", } fmt.Printf("%#vn", a) fmt.Println(a.N, a.s) Playgroundで動かす 構造体リテラル 型情報 フィールド
  31. 31. typeを使った型の作成 type <型名> <型リテラル>|<既存の型> 型・メソッド・インタフェース/typeを使った型の作成 31 // 組み込み型に名前をつける type Int int // 他のパッケージの型に名前をつける type MyWriter io.Writer // 型リテラルに名前をつける type Person struct { Name string } intとIntは別の型として扱われる
  32. 32. typeで名前が付けれるもの ■ 組み込み型 int, float64, string など ■ 型リテラル 構造体、インタフェース、 マップ、スライス、チャネル、関数 など ■ 名前付きの型 パッケージの内外で作った型 型・メソッド・インタフェース/typeで名前を付けれるもの 32 別の型として再定義できる
  33. 33. メソッド −1− type で定義した型はメソッドのレシーバにできる 型・メソッド・インタフェース/メソッド −1− 33 type Hex int func (h Hex) String() string { return fmt.Sprintf("%x", int(h)) } // 100をHex型として代入 var hex Hex = 100 // Stringメソッドを呼び出す fmt.Println(hex.String()) Playgroundで動かす
  34. 34. メソッド −2− ■ レシーバにできるもの ● 名前の付いた型 typeで定義したもの ● パッケージ内の型のみ パッケージ外の型はtypeで再定義する ● ポインタ型も含む レシーバに変更を与えたい場合 レシーバも引数と同じ扱い 型・メソッド・インタフェース/メソッド −2− 34 type Hex int など type S bufio.Scanner など func (p *Hoge) M() { ... } など
  35. 35. メソッド −3− ■ 課題3 関数にメソッドを設けてみよう。 var f func(string) int ■ 課題4 レシーバをポインタにしてみてレシーバに変更を与 えてみよう。構造体以外も試してみよう。 ■ 課題5 レシーバがnilの場合の挙動を試してみよう。 型・メソッド・インタフェース/メソッド −3− 35
  36. 36. インタフェース ● メソッドのリストを持つ ● メソッドのリストがインタフェースで規定しているものと一致 する型はインタフェースを実装していることになる 型・メソッド・インタフェース/インタフェース 36 var s interface { String() string } s = Hex(100) fmt.Println(s.String()) type Hex int func (h Hex) String() string { return fmt.Sprintf("%x", int(h)) } Playgroundで動かす インタフェースを実装していることになる
  37. 37. 型とメソッドとインタフェース ■ 既存の型にもインタフェースを実装 ● 後づけで実装させる ● メソッドリストさえ一致してればよい ■ 構造体以外も実装可能 ● typeで宣言すればメソッドが設けられる ● メソッドリストさえ一致してればよい 型・メソッド・インタフェース/型とメソッドとインタフェース 37
  38. 38. インタフェースの活用 ■ 1つのメソッドしか持たない ● io.Writer: Writeメソッド ● io.Reader: Readメソッド ■ 標準パッケージで多く使われている ● fmt, net, bytes, encoding, bufio, os ... ● ファイルやネットワークのコネクション ● 抽象度の高いインタフェース 型・メソッド・インタフェース/インタフェースの活用 38 インタフェースは Goの良い言語機能の1つ
  39. 39. インタフェース ■ 課題6 interface{} という型はどういう特徴を持つ型 か説明してください。 ■ 課題7 インタフェース型を1つ作り、組み込み型、構造体 型、関数型にそれぞれ実装させてみましょう。ま た、作ったインタフェース型を引数に取る関数を 作ってみましょう。 型・メソッド・インタフェース/インタフェース 39
  40. 40. 型アサーション インタフェース.(型) インタフェース型の値を任意の型にキャストする。第2戻り値に キャストできるかどうかが返る。 型・メソッド・インタフェース/型アサーション 40 var v interface{} v = 100 n,ok := v.(int) fmt.Println(n, ok) s,ok := v.(string) fmt.Println(s, ok) Playgroundで動かす
  41. 41. 型スイッチ 型によって処理をスイッチする 型・メソッド・インタフェース/型スイッチ 41 var i interface{} i = 100 switch v := i.(type) { case int: fmt.Println(v*2) case string: fmt.Println(v+"hoge") default: fmt.Println("default") } Playgroundで動かす インタフェース i = "hoge" も試してみよう
  42. 42. 埋め込み −1− 構造体に匿名フィールドを埋め込む機能 型・メソッド・インタフェース/埋め込み −1− 42 type Hoge struct { N int } // Fuga型にHoge型を埋め込む type Fuga struct { Hoge // 名前のないフィールドになる }
  43. 43. 埋め込み −2− 埋め込んだ値に移譲(継承とは違う) 型・メソッド・インタフェース/埋め込み −2− 43 type Hoge struct {N int} type Fuga struct {Hoge} f := Fuga{Hoge{N:100}} // Hoge型のフィールドにアクセスできる fmt.Println(f.N) // 型名を指定してアクセスできる fmt.Println(f.Hoge.N) Playgroundで動かす
  44. 44. 埋め込みの特徴 ■ 型リテラルでなければ埋め込められる ● typeで定義したものや組み込み型 ● インタフェースも埋め込められる ■ インタフェースの実装 埋め込んだ値のメソッドもカウント 型・メソッド・インタフェース/埋め込みの特徴 44 // Stringerを実装 type Hex int func (h Hex) String() string { return fmt.Sprintf("%x", int(h)) } // Hex2もStringerを実装 type Hex2 struct {Hex} type Stringer interface { String() string } Playgroundで動かす
  45. 45. インタフェースと埋め込み ■ 既存のインタフェースの振る舞いを変える 型・メソッド・インタフェース/インタフェースと埋め込み 45 type Hoge interface{M();N()} type fuga struct {Hoge} func (f fuga) M() { fmt.Println("Hi") f.Hoge.M() // 元のメソッドを呼ぶ } func HiHoge(h Hoge) Hoge { return fuga{h} // 構造体作る } Mの振る舞いを変える
  46. 46. インタフェースと埋め込み ■ 課題8 以下のコードは有効でしょうか?Playgroundで動 かして確認しましょう。 ■ 課題9 前のスライドの例を実際にPlaygroundで動かして 挙動を確認しよう。 型・メソッド・インタフェース/インタフェースと埋め込み 46 type Hoge struct {N int} type Fuga struct {Hoge} f := Fuga{Hoge{100}} var _ Hoge = f _は変数を使用しないときに使う記法 HiHogeの戻り値の型が Hogeにできる理由は? 参考:インタフェースの実装パターン
  47. 47. ゴールーチン・チャネル ● 並行と並列 ● ゴールーチン ● チャネル ● チャネルを使うパターン 47
  48. 48. Concurrency is not Parallelism ■ 並行と並列は別ものである by RobPike ● 並行:Concurrency ● 並列:Parallelism ■ Concurrency ● 同時にいくつかの質の異なることを扱う ■ Parallelism ● 同時にいくつかの質の同じことを扱う ゴールーチン・チャネル/Concurrency is not Parallelism 48
  49. 49. 並列と並行の違い ■ Concurrency  同時にいくつかの質の異なることを扱う ■ Parallelism  同時にいくつかの質の同じことを扱う ゴールーチン・チャネル/並行と並列の違い 49 本を運ぶ 本を燃やす 台車を戻す 本を積む 本を燃やす 本を燃やす 本を燃やす
  50. 50. ゴールーチンとConcurrency ■ ゴールーチンでConcurrencyを実現 ● 複数のゴールーチンで同時に複数のタスクをこなす ● 各ゴールーチンに役割を与えて分業する ■ 軽量なスレッドのようなもの ● LinuxやUnixのスレッドよりコストが低い ● 1つのスレッドの上で複数のゴールーチンが動く ■ ゴールーチンの作り方 ● goキーワードをつけて関数を呼び出す ゴールーチン・チャネル/ゴールーチンとConcurrency 50 複数のコアで動くとは限らない go f()
  51. 51. 無名関数とゴールーチン ゴールーチン・チャネル/無名関数とゴールーチン 51 package main import "fmt" import "time" func main() { go func() { fmt.Println("別のゴールーチン") }() fmt.Println("mainゴールーチン") time.Sleep(50*time.Millisecond) } Sleepしないとすぐに終了する http://play.golang.org/p/jy1HWriRTS
  52. 52. ゴールーチン間のデータのやりとり −1− ゴールーチン・チャネル/ゴールーチン間のデータのやりとり −1− 52 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1()
  53. 53. ゴールーチン間のデータのやりとり −2− ゴールーチン・チャネル/ゴールーチン間のデータのやりとり −2− 53 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() 変数v print(v) v = 100 共有の変数を使う?
  54. 54. ゴールーチン間で共有の変数を使う ゴールーチン・チャネル/ゴールーチン間で共有の変数を使う 54 func main() { done := false go func() { time.Sleep(3 * time.Second) done = true }() for !done { time.Sleep(time.Millisecond) } fmt.Println("done!") } 共有の変数を使う http://play.golang.org/p/mGSOaq4mcr
  55. 55. ゴールーチン間のデータ競合 −1− ゴールーチン・チャネル/ゴールーチン間のデータ競合 −1− 55 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() 変数v print(v) v = 100 処理順序が保証されない 競合
  56. 56. ゴールーチン間のデータ競合 −2− ゴールーチン・チャネル/ゴールーチン間のデータ競合 −2− 56 n := 1 go func() { for i := 2; i <= 5; i++ { fmt.Println(n, "*", i) n *= i time.Sleep(100) } }() http://play.golang.org/p/yqk82u0E4V for i := 1; i <= 10; i++ { fmt.Println(n, "+", i) n += 1 time.Sleep(100) } 競合
  57. 57. データ競合の解決 ■ 問題点 ● どのゴールーチンが先にアクセスするか分からない ● 値の変更や参照が競合する ■ 解決方法 ● 1つの変数には1つのゴールーチンからアクセスする ● チャネルを使ってゴールーチン間で通信をする ● またはロックをとる(syncパッケージ) ゴールーチン・チャネル/データ競合の解決 57 "Do not communicate by sharing memory; instead, share memory by communicating"
  58. 58. チャネルとは? ゴールーチン・チャネル/チャネルとは? 58 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() ch<-100<-ch チャネル 100
  59. 59. チャネルの特徴 ■ 送受信できる型 ● チャネルを定義する際に型を指定する ■ バッファ ● チャネルにバッファを持たせることができる ● 初期化時に指定できる ● 指定しないと容量0となる ■ 送受信時の処理のブロック ● 送信時にチャネルのバッファが一杯だとブロック ● 受信時にチャネル内が空だとブロック ゴールーチン・チャネル/チャネルの特徴 59
  60. 60. 送信時のブロック ゴールーチン・チャネル/送信時のブロック 60 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() 受信してくれるまでブロック ch<-100 チャネル 100 ブロック
  61. 61. 受信時のブロック ゴールーチン・チャネル/受信時のブロック 61 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() 送信されるまでブロック <-ch チャネル 100 ブロック
  62. 62. チャネルの基本 −1− ゴールーチン・チャネル/チャネルの基本 −1− 62 ■ 初期化 ■ 送信 ■ 受信 ch1 := make(chan int) ch2 := make(chan int, 10) n1 := <-ch1 n2 := <-ch2 + 100 容量を指定 ch1 <- 10 ch2 <- 10 + 20 受け取られるまでブロック 一杯であればブロック 送信されまでブロック 空であればブロック make(chan int, 0)と同じ
  63. 63. チャネルの基本 −2− ゴールーチン・チャネル/チャネルの基本 −2− 63 func main() { done := make(chan bool) // 容量0 go func() { time.Sleep(time.Second * 3) done <- true }() <-done fmt.Println("done") } 送信されるまでブロック http://play.golang.org/p/k0sMCYe4PA
  64. 64. 複数のチャネルから同時に受信 ゴールーチン・チャネル/複数のチャネルから同時に受信 64 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() チャネル-1 チャネル-2 ブロック ブロックされるので 同時に送受信出来ない?
  65. 65. select - case −1− ゴールーチン・チャネル/select-case −1− 65 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() チャネル-1 チャネル-2 ブロックされるので 同時に送受信出来ない? select
  66. 66. select - case −2− ゴールーチン・チャネル/select - case −2− 66 func main() { ch1 := make(chan int) ch2 := make(chan string) go func() { ch1<-100 }() go func() { ch2<-"hi" }() select { case v1 := <-ch1: fmt.Println(v1) case v2 := <-ch2: fmt.Println(v2) } } 先に受信した方を処理 http://play.golang.org/p/moVwtEdQIv
  67. 67. nilチャネル ゴールーチン・チャネル/nilチャネル 67 func main() { ch1 := make(chan int) var ch2 chan string go func() { ch1<-100 }() go func() { ch2<-"hi" }() select { case v1 := <-ch1: fmt.Println(v1) case v2 := <-ch2: fmt.Println(v2) } } nilの場合は無視される ゼロ値はnil http://play.golang.org/p/UcqW6WH0XT
  68. 68. ファーストクラスオブジェクト ■ チャネルはファーストクラスオブジェクト ● 変数に入れれる ● 引数に渡す ● 戻り値で返す ● チャネルのチャネル ■ timeパッケージ ゴールーチン・チャネル/ファーストクラスオブジェクト 68 http://golang.org/pkg/time/#After chan chan int など // 5分間待つ <-time.After(5 * time.Minute) 5分たったら現在時刻が 送られてくるチャネルを返す
  69. 69. チャネルを引数や戻り値にする ゴールーチン・チャネル/チャネルを引数や戻り値にする 69 func makeCh() chan int { return make(chan int) } func recvCh(recv chan int) int { return <-recv } func main() { ch := makeCh() go func() { ch <- 100 } fmt.Println(recvCh(ch)) } http://play.golang.org/p/UcqW6WH0XT
  70. 70. 双方向チャネル ゴールーチン・チャネル/双方向チャネル 70 func makeCh() chan int { return make(chan int) } func recvCh(recv chan int) int { go func() { recv <- 200 }() return <-recv } func main() { ch := makeCh() go func() { ch <- 100 }() fmt.Println(recvCh(ch)) } http://play.golang.org/p/6gU92C6Q2v 間違った使い方ができる
  71. 71. 単方向チャネル ゴールーチン・チャネル/単方向チャネル 71 func makeCh() chan int { return make(chan int) } func recvCh(recv <-chan int) int { return <-recv } func main() { ch := makeCh() go func(ch chan<- int) { ch <- 100 }(ch) fmt.Println(recvCh(ch)) } http://play.golang.org/p/pY4u1PU3SU 受信専用のチャネル 送信専用のチャネル
  72. 72. タイピングゲーム ■ 課題10 90秒以内に予め用意された5つの文章を入力させ、すべて入力 できた場合は"OK"と出力し、タイムオーバーの場合は"Time Over"と表示するプログラムを作ってください。なお、入力ミスし た場合は正解するまで同じ文章を入力させてください。 ヒント: time.After, bufio.Scanner ゴールーチン・チャネル/タイピングゲーム 72
  73. 73. Concurrencyの実現 ■ 複数のゴールーチンで分業する ● タスクの種類によってゴールーチンを作る ● Concurrencyを実現 ■ チャネルでやりとりする ● ゴールーチン間はチャネルで値を共有する ● 複雑すぎる場合はロックを使うことも ■ for-selectパターン ● ゴールーチンごとに無限ループを作る ● メインのゴールーチンはselectで結果を受信 ゴールーチン・チャネル/Concurrencyの実現 73
  74. 74. for-selectパターン −1− ゴールーチン・チャネル/for-selectパターン −1− 74 ゴールーチン-main ゴールーチン-2 go f2() ゴールーチン-1 go f1() チャネル-1 チャネル-2 select for{}for{} 各ゴールーチンで 無限ループを作る
  75. 75. for-selectパターン −2− ゴールーチン・チャネル/for-selectパターン −2− 75 ゴルーチン-1 for{} ゴルーチン-2 for{} ゴルーチン-3 for{} ゴルーチン-4 for{} チャネル チャネル チャネル チャネル
  76. 76. GopherでConcurrency ■ 課題11 前のスライドのGopherたちが本を燃やす様子をプログラムで表 現してみてください。なお、1冊の本を燃やしたり、本を積んだ り、台車を運んだりするのに、それなりに時間がかかることを想 定し、適度にtime.Sleepで処理を止めて見ましょう。 ■ 課題12 課題11で燃やす本の量をどんどん増やした場合に、どうス ケールすれば処理速度を落とさずに本を燃やせるでしょうか? ゴールーチン・チャネル/GopherでConcurrency 76 # データ競合のチェック go run -race main.go
  77. 77. ネットワークプログラミング ● netパッケージ ● net/httpパッケージ 77
  78. 78. netパッケージ −サーバ− ネットワークプログラミング/netパッケージ −サーバー− 78 p, a := "tcp", ":8080" ln, err := net.Listen(p, a) if err != nil {...} for { conn, err := ln.Accept() if err != nil {...} go handle(conn) } エラー処理 エラー処理
  79. 79. netパッケージ −クライアント− ネットワークプログラミング/netパッケージ −クライアント− 79 p, a := "tcp", ":8080" conn, err := net.Dial(p, a) if err != nil {...} // 書き込み fmt.Fprintln(conn, "hello") エラー処理 手元で試してみよう! (クライアント、サーバ) グループチャットも 作ってみよう! (プログラミング言語Goの8.10も参考になります。)
  80. 80. net/httpパッケージ ネットワークプログラミング/net/httpパッケージ 80 h := func( w http.ReponseWriter, r *http.Request) { fmt.Fprintf(w, "hello") } http.HandleFunc("/hello", h) const a = ":8080" http.ListenAndServe(a, nil) 手元で試してみよう!
  81. 81. Android上でサーバを動かす 81 ネットワークプログラミング/Android上でサーバを動かす Youtubeで見る コード 母艦のシェル adb shell 端末
  82. 82. タイピングゲーム2 ■ 課題13 課題11のタイピングゲームを改造し、netパッケージを使って 立てたtcpのサーバから問題となる文章と制限時間を取得し、そ の時間内にタイピングできるかを競うゲームにしてみよう。 ■ 課題14 上記のプログラムをサーバとクライアントで実行可能ファイルを 分けずに、1つの実行ファイルでサーバもクライアントも実現しよ う。また、問題の出題を交互に行えるようにしよう。 ネットワークプログラミング/タイピングゲーム2 82
  83. 83. go test とtestingパッケージ ● go test ● testingパッケージ ● コードの品質とGo 83
  84. 84. go test go test と testingパッケージ/go test 84 ■ testを行うためコマンド  _testというサフィックスの付いた  goファイルを対象にしてテストを実行 # mypkgのテスト行う $ go test mypkg ok mypkg 0.007s # 失敗する場合 $ go test mypkg --- FAIL: TestHex_String (0.00s) hex_test.go:11: expect="a" actual="A" FAIL FAIL mypkg 0.008s hex.go ⇒ hex_test.go
  85. 85. go testのオプション(一部) ■ -v 詳細を表示する。 ■ -cpu 実行する並列度を指定する。 複数のコアを使ったテストができる。 ■ -race データの競合が起きないかテストする。 ■ -cover カバレッジを取得する。 go test と testingパッケージ/go testのオプション(一部) 85
  86. 86. testingパッケージ ■ テストを行うため機能を提供するパッケージ  *testing.T型のメソッド使う。 go test と testingパッケージ/testingパッケージ 86 package mypkg_test import "testing" import "mypkg" func TestHex_String(t *testing.T) { expect := "a" actual := mypkg.Hex(10).String() if actual != expect { t.Errorf(`expect="%s" actual="%s"`, expect, actual) } } type Hex int func (h Hex) String() string { return fmt.Sprintf("%x", int(h)) } mypkg.go mypkg_test.go
  87. 87. testingパッケージでできること ■ 失敗理由を出力してテストを失敗させる  Error(), Errorf(),  Fatal(), Fatalf() ■ テストの並列実行 Parallel() go testの-parallelオプションで並列数を指定 ■ ベンチマーク  *testing.B型を使う ■ ブラックボックステスト testing/quickパッケージ go test と testingパッケージ/testingパッケージでできること 87
  88. 88. testingパッケージでできないこと ■ アサーションはない 自動でエラーメッセージを作るのではなく、 ひとつずつErrorfを使って自前で作る ■ テストはGoで書く テストのための新しいミニ言語を作らない ■ 比較演算子だけはツライのでは? reflect.DeepEqualを使うと良い go test と testingパッケージ/testingパッケージでできないこと 88 かなり薄いテストパッケージ
  89. 89. Go Mock ■ インタフェースのモックを作るツール github.com/golang/mock/gomock メソッドが呼ばれているかなどがテストできる。 標準パッケージではなく、サブプロジェクト。 func TestSample(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() m := mock.NewMockSample(ctrl) m.EXPECT().Method("hoge").Return(1) t.Log("result:", m.Method("hoge")) } 参考:Go Mockでインタフェースのモックを作ってテストする go test と testingパッケージ/Go Mock 89
  90. 90. Goのコンセプト ■ 実現する手段は少ないほうが良い  機能が増えると複雑さが増える。 ■ 暗黙的で曖昧な記述をさせない  エラーにつながる,暗黙の型変換や  不使用の変数等の宣言は許さない。 ■ 不要なものは避ける  不要な型の宣言の排除など,  タイプ数をできるだけ減らすように。 ■ コンセプトに一貫性を持たせる  コンセプトにずれる言語仕様は入れない。 go test と testingパッケージ/Goのコンセプト 90 “Simplicity is Complicated” by Rob Pike
  91. 91. シンプルさと強力さ ■ シンプルな機能を組み合わせる  シンプルな機能を組み合わせて,複雑な問題に対処する   ⇒シンプルだが強力さも十分ある go test と testingパッケージ/シンプルさと強力さ 91 シンプル 簡潔さ 強力さ 表現力 品質の良いコードが作りやすい
  92. 92. Goのコード品質を高める要素 ■ シンプルな文法と言語設計  可読性の高い文法と複雑になりにくい言語仕様。 ■ 型階層がない  複雑な型の階層が存在せず不要な型ができにくい。 ■ コンパイルによるエラー検出  静的型付け言語なのでバグがコンパイル時に分かる。  バグになり得る箇所がコンパイルエラーになる。  (型不一致,未使用の変数など) ■ コードフォーマッタ  標準のコードフォーマッタ(gofmt)がある。 ■ テスト  標準のテストツール(go test)がある go test と testingパッケージ/Goのコード品質を高める要素 92
  93. 93. コンパイルエラーになるもの −1− ■ 型の不一致 go test と testingパッケージ/コンパイルエラーになるもの −1− 93 var n int = 100 var m float64 = 1.5 // エラー var a int = n + m // OK var b int = n + int(m)
  94. 94. コンパイルエラーになるもの −2− ■ 未使用の変数/パッケージ go test と testingパッケージ/コンパイルエラーになるもの −2− 94 import ( "fmt" // エラー _ "io" // OK ) func main() { var n int = 100 // エラー _ = 200 // OK }
  95. 95. コンパイルエラーになるもの −3− ■ インタフェースの未実装(型の不一致) go test と testingパッケージ/コンパイルエラーになるもの −3− 95 type Hex int func (h Hex) Str() string { return fmt.Sprintf("%x", int(h)) } // エラー var _ fmt.Stringer = Hex(100) type Stringer interface { String() string }
  96. 96. コンパイルエラーになるもの −4− ■ 曖昧な記述 go test と testingパッケージ/コンパイルエラーになるもの −4− 96 type Hoge struct{ N int } type Piyo struct{ N int } type Foo struct { Hoge Piyo } func main() { f := Foo{Hoge{100}, Piyo{200}} fmt.Println(f.N) // エラー fmt.Println(f.Hoge.N) // OK }
  97. 97. Goとテスト ■ 言語のコンセプトを守る ● 実現する手段は少なく ⇒ テストの為のミニ言語を入れない。 ● 暗黙的で曖昧な記述をさせない ⇒ アサーションで自動でエラーメッセージを作らない。  コンテキストにあったエラーメッセージを作る。 ■ コンパイルエラーで検出できる コンパイルで検出できるものはテストは不要。 コンパイルでは検出できないものに集中できる。 ■ テストが良いサンプル テストがGoで書かれてるので良いサンプルになる。 go test と testingパッケージ/Goとテスト 97 FAQを読もう!
  98. 98. ドキュメントとテスト ■ テストされたサンプル func ExampleHex_String() { fmt.Println(mypkg.Hex(10)) // Output: a } テストファイルにExampleで始まる 関数を書くとサンプルとして扱われる。 // Output:を書くとテスト対象になる。 go test と testingパッケージ/ドキュメントとテスト 98
  99. 99. 言語標準ツールの強み ■ ツール間で連携が取りやすい 標準ツールなので、他のツールと連携が取りやすい ■ メンテが保証される  バグが放置されたり、メンテされなかったりしない ■ みんなが共通に使う その言語を使っているユーザ間で、共通の知識になる テストツールも 言語標準のメリットは大きい go test と testingパッケージ/言語標準ツールの強み 99
  100. 100. 課題 ■ 課題15 hogeパッケージのテスト書いてみましょう。 ■ 課題16 Exapleテストを書いてみましょう。 また、godocを使ってドキュメント生成してみましょう。 go test と testingパッケージ/課題 100 # godocをインストールしよう $ go get golang.org/x/tools/cmd/godoc $ $GOPATH/bin/godoc --http=":8080"
  101. 101. リフレクション ● reflectパッケージとは ● リフレクションの基本 101
  102. 102. reflectパッケージとは? ■ 何ができるのか? ● 実行時に型情報を取得 ● 任意の型の変数に値を入れる ● 構造体のフィールドのタグを取得する ■ どこで使われてるの? ● encodingパッケージ ● ORマッパーなど リフレクション/reflectパッケージとは? 102
  103. 103. encodingパッケージでの利用 ● JSONなどのシリアライズされたものを構造体に 変換する際に使用される type Person struct { Name string `json:"name"` Aget int `json:"age"` } { "name": "Gopher", "age": 4 } Goの構造体: JSON例: structタグで対応付ける リフレクション/encodingパッケージでの利用
  104. 104. templateパッケージでの利用 ● HTMLなどに任意の型の値を埋め込むために 使われる Hello, {{.Name}}!! Hello, Gopher!! Person { Name: “Gopher”, Age: 4, } テンプレート 出力 データの埋込み Execute リフレクション/templateパッケージでの利用
  105. 105. Value型とType型 ■ Value型 ● 任意の値を表す型 ● 値への操作をメソッドで提供 ● reflect.ValueOf()で取得できる ■ Type型 ● 任意の型を表す型 ● 型に関する操作をメソッドで提供 ● reflect.TypeOf()で取得できる 105 リフレクション/Value型とType型
  106. 106. 変数に値を入れる 106 var n int fmt.Println(n) // 0 vp := reflect.ValueOf(&n) v := vp.Elem() if v.CanSet() { v.SetInt(100) } fmt.Println(n) // 100 http://play.golang.org/p/HkJPjQsP8o リフレクション/変数に値を入れる
  107. 107. 変数に値を入れる(図解) 100n: &n Value (ポインタ) ValueOf() Value (Int) Elem() Set リフレクション/変数に値を入れる(図解)
  108. 108. interface{}としてポインタを渡す ■ interface{}として、任意の型のポインタを受 け取る func set(p, v interface{}) { pv := reflect.ValueOf(p) // ポインタ vv := reflect.ValueOf(v) // 設定する値 pv.Elem().Set(vv) } リフレクション/interface{}としてポインタを渡す
  109. 109. 構造体のリフレクション 109 s := struct{ A string `k:"v"`; b int }{"a", 1} v := reflect.ValueOf(&s).Elem() println(v.FieldByName("A").CanSet()) println(v.FieldByName("b").CanSet()) f1, ok := v.Type().FieldByName("A") println(ok, f1.PkgPath, f1.Tag.Get("k")) f2, _ := v.Type().FieldByName("b") println(f2.PkgPath) http://play.golang.org/p/NkwP3KSjDu リフレクション/構造体のリフレクション
  110. 110. リフレクションの注意点 ■ 容易にpanicが起きる ● ValueとTypeで実体によって対応していな いメソッドを呼ぶとpanicになる ○ 例:int型の値のValueに対してLen()を呼ぶ ● Kindで適切に分岐してpanicを避ける ■ 実行時間がかかる ● 実行時に解析するのでコストが大きい ● 静的解析を用いる選択肢もある リフレクション/リフレクションの注意点
  111. 111. 静的解析 ● 静的解析とは? ● Goにおける静的解析 ● 静的解析の基本 111
  112. 112. ソースコードの静的解析とは? 112 ■ ソースコードを実行せずに解析すること ● ソースコードから抽象構文木(AST)などを取 得して解析する ● 静的型付け言語だと、静的解析で型情報が 取得できる ● 逆は実行して解析する動的解析 静的解析/ソースコードの静的解析とは?
  113. 113. Goで静的解析をすると何が嬉しいのか? ● リファクタリングツール ○ 変数の宣言位置や使用箇所の抽出 ○ パッケージの解析 ● コードジェネレーター ○ コメントによるアノテーションの抽出 ○ コードフォーマッタ ● 処理系 ○ 抽象構文木(AST)の解析 ○ 定数の扱い 113 静的型付け言語なので 静的解析でも多くの事が知れる 静的解析/Goで静的解析をすると何が嬉しいのか?
  114. 114. 開発ツールとソースコードの静的解析 114 ■ 開発ツールの多くは静的解析を行っている ● gofmt/goimports ○ コードフォーマッター ● go vet/golint ○ コードチェッカー、リンター ● guru ○ 静的解析 ● gocode ○ コード補完 ● errcheck ○ エラー処理のチェック ● gorename/gomvpkg ○ リファクタリングツール 静的解析/開発ツールとソースコードの静的解析
  115. 115. ■ 標準パッケージで静的解析の機能を提供 goパッケージ 115 go/ast 抽象構文木(AST)を提供 go/build パッケージに関する情報を集める go/constant 定数に関する型を提供 go/doc ドキュメントをASTから取り出す go/format コードフォーマッタの機能を提供 go/importer コンパイラに適したImporterを提供 go/parser 構文解析の機能を提供 go/printer ASTの表示機能を提供 go/scanner 字句解析の機能を提供 go/token トークンに関する型を提供 go/types 型チェックに関する機能を提供 静的解析/goパッケージ
  116. 116. 静的解析の流れ 116 ソースコード トークン 抽象構文木(AST) 型情報 構文解析 字句解析 型チェック go/scanner go/token go/parser go/ast go/types go/constant 静的解析/静的解析の流れ
  117. 117. 字句解析 - go/scanner,go/token ■ 文字列をトークンにしていく ● 空白などを取り除き、意味のある単位=トー クンにしていく作業 117 v + 1 IDENT ADD INT トークン ソースコード 静的解析/字句解析
  118. 118. 構文解析 - go/parser,go/ast ■ トークンを抽象構文木(AST)にしていく ● プログラムの構造を持たせる 118 v + 1 IDENT ADD INT ソースコード + v 1 BinaryExpr Ident BasicLit トークン 抽象構文木(AST) 静的解析/構文解析
  119. 119. 型チェック - go/types,go/constant ■ 型チェックを行う ● 識別子の解決 ● 型の推論 ● 定数の評価 119 n := 100 + 200 fmt.Println(n) 定数の評価 =300 型の推論 -> int 識別子の解決 識別子の解決 -> fmtパッケージ 静的解析/型チェック
  120. 120. 抽象構文木(ASt)の取得 ■ go/parserパッケージの関数を使う ● ParseExpr,ParseExprFrom ○ 式をパースする ○ ParseExprはParseExprFromを簡易版 ● ParseFile ○ ファイル単位でパースする ● ParseDir ○ ディレクトリ単位でパースする ○ 中でParseFileを呼んでいる 120 静的解析/抽象構文木(AST)の取得
  121. 121. 式のASTを取得する ■ 式を構文解析する ■ ParseExprFromでも書ける 121 expr, err := parser.ParseExpr(`v + 1`) if err != nil { /* エラー処理 */ } /* exprを解析する処理 */ fset := token.NewFileSet() // ファイル情報 src := []byte(`v + 1`) f := "" // ファイル名(式なので不要) m := 0 // モード(式なので不要) expr, err := parser.ParseExprFrom(fset, f, s, m) 静的解析/式のASTを取得する
  122. 122. token.FileSetとは? ■ ファイル中の位置情報を記録する為の型 ● 位置情報は数値で表される ● 複数のファイル間で一意の値 ● 各ファイルのoffsetが記録されている ● パースする際に記録されていく 122 token.FileSetは出力引数として Parse系の関数に渡す 静的解析/token.FileSetとは?
  123. 123. ファイルからASTを取得する ■ 完全なGoのソースコードを構文解析する 123 const src = ` package main var v = 100 func main() { fmt.Println(v+1) }` fs := token.NewFileSet() f, err := parser.ParseFile(fs, "my.go", src, 0) if err != nil { /* エラー処理 */ } /* f を解析する処理 引数はparse.ExprFromと 同じ構成 srcがnilだとファイル名 でファイルを開く 解析するファイルの中身 静的解析/ファイルからASTを取得する
  124. 124. Hello, WorldのASTの構成 124 package main import "fmt" func main() { fmt.Println("Hello, 世界") } ast.File ast.File ast.GenDecl ast.FuncDecl ast.CallExpr Goの抽象構文木(AST)を手入力してHello, Worldを作る http://qiita.com/tenntenn/items/0cbc6f1f00dc579fcd8c Playgroundで動かす 静的解析/Hello, WorldのASTの構成
  125. 125. ASTをトラバースする ■ ast.Inspectを使う 125 n, _ := parser.ParseExpr(`v + 1`) ast.Inspect(n, func(n ast.Node) bool { if n != nil { fmt.Printf("%Tn", n) } return true }) printer.Fprint(os.Stdout, token.NewFileSet(), n) *ast.BinaryExpr *ast.Ident *ast.BasicLit v + 1 + v 1 構文解析 抽象構文木(AST)を探索 抽象構文木(AST)を出力 BinaryExpr Ident BasicLit Playgroundで動かす ast.Walkというのもある 静的解析/ASTをトラバースする
  126. 126. ASTをトラバースする ■ 再帰を使ってトラバースする 126 func traverse(n ast.Node) { switch n := n.(type) { case *ast.Indent: fmt.Println(n.Name) case *ast.BinaryExpr: traverse(n.X) traverse(n.Y) case *ast.UnaryExpr: traverse(n.X) } } 識別子の場合は名前を出力 二項演算式の場合は 各項を探索 単項演算式の場合は 項を探索 型でswitchする https://play.golang.org/p/5SOdiy420p 静的解析/ASTをトラバースする
  127. 127. 参考資料 ■ goパッケージで簡単に静的解析して世界を広げよう ● コードジェネレータ ○ ASTを取得する方法を調べる ○ 抽象構文木(AST)をトラバースする ○ 抽象構文木(AST)をいじってフォーマットをかける ○ Goの抽象構文木(AST)を手入力してHello, Worldを作る ○ go-app-builderのソースコードを読む ● リファクタリングツール ○ gorenameをライブラリとして使う ○ Goのスコープについて考えてみよう ○ go/typesパッケージを使い変数名をリネームしてみる ● 処理系 ○ 簡単な式の評価機を作ってみる ○ 【実践goパッケージ】文字列から複素数型の値をパースする ○ もっと楽して式の評価器を作る 127 静的解析/参考資料
  128. 128. ハンズオン ● ハンズオンの説明 ● ドキュメントとPlayground 128
  129. 129. ハンズオンの説明 ■ リポジトリ ● https://github.com/tenntenn/gohandson/tree/master/i mgconv/ja ■ コマンドラインツール ● ターミナルで動くプログラム ● 画像を変換するツール # 50%に縮小して、JPEGにする $ imgconv -resize 50% a.png b.jpg ハンズオン/ハンズオンの説明 129
  130. 130. ドキュメントを読もう ■ パッケージドキュメント ● https://golang.org/pkg ● 標準パッケージの使い方が書いてある ■ FAQ ● https://golang.org/doc/faq ● なぜGoに◯◯がないのか?など ■ 言語仕様 ● https://golang.org/ref/spec 公式ドキュメントを読もう!! ハンズオン/ドキュメントを読もう 130
  131. 131. Go Playground ■ Go Playground ● http://play.golang.org/ ● Web上でGoを実行できる ● Share機能で、SNSで共有したり質問する ハンズオン/Go Playground 131

×