2016年度 Go研修

1,027 views
141 views

Published on

この資料はKLab株式会社の新卒研修の資料です。

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,027
On SlideShare
0
From Embeds
0
Number of Embeds
56
Actions
Shares
0
Downloads
1
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

2016年度 Go研修

  1. 1. 2016年度 Go研修 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. ※この資料はKLab株式会社の 新卒研修の資料です
  2. 2. アジェンダ ■ 講師自己紹介 ■ Goの紹介 ■ Goの基本 ■ 型・メソッド・インタフェース ■ ゴールーチン・チャネル ■ ネットワークプログラミング ■ go test と testingパッケージ ■ ハンズオン 2
  3. 3. 自己紹介 KLab株式会社 KLabGames事業本部 エンジニア 上田拓也 twitter: @tenntenn ■ 好きな言語 Go, JavaScript, Lua ■ 業務 クライアントチーム(Unity) 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の勉強するには? ■ コミュニティ ● KLab内の社内チャット ○ 社内チャットだけど、割りとコアな話ができる ● gophers-slack ○ 世界中のGopherが集まるチャット ● Qiita #Go ○ Go言語の初心者が見ると幸せになれる場所 ■ 書籍 ● The Go Programing Language(日本語) 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. ハンズオン ● ハンズオンの説明 ● ドキュメントとPlayground 101
  102. 102. ハンズオンの説明 ■ リポジトリ ● https://github. com/tenntenn/gohandson/tree/master/imgconv/ja ■ コマンドラインツール ● ターミナルで動くプログラム ● 画像を変換するツール # 50%に縮小して、JPEGにする $ imgconv -resize 50% a.png b.jpg ハンズオン/ハンズオンの説明 102
  103. 103. ドキュメントを読もう ■ パッケージドキュメント ● https://golang.org/pkg ● 標準パッケージの使い方が書いてある ■ FAQ ● https://golang.org/doc/faq ● なぜGoに◯◯がないのか?など ■ 言語仕様 ● https://golang.org/ref/spec 公式ドキュメントを読もう!! ハンズオン/ドキュメントを読もう 103
  104. 104. Go Playground ■ Go Playground ● http://play.golang.org/ ● Web上でGoを実行できる ● Share機能で、SNSで共有したり質問する ハンズオン/Go Playground 104

×