2016年度 go研修

131
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.

Upload: takuya-ueda

Post on 16-Apr-2017

15.069 views

Category:

Technology


0 download

TRANSCRIPT

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.

アジェンダ

■ 自己紹介

■ Goの紹介

■ Goの基本

■ 型・メソッド・インタフェース

■ ゴールーチン・チャネル

■ ネットワークプログラミング

■ go test と testingパッケージ

■ ハンズオン

2

自己紹介

上田拓也twitter: @tenntenn

■ コミュニティ活動Google Cloud Platform User Group (GCPUG) TokyoGoビギナーズgolang.tokyoGo Conference

3

Goの紹介

● Goとは?

● Goの特徴

● Goを勉強するには?

4

Goとは?

Googleが開発しているプログラミング言語

■ 特徴

● シングルバイナリ・クロスコンパイル

● 強力でシンプルな言語設計と文法

● 並行プログラミング

● 豊富な標準ライブラリ群

● 周辺ツールの充実

Goの紹介/Goとは? 5

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はコンパイルするコマンド

Goの特徴 − 強力でシンプルな言語設計と文法 −

■ スクリプト言語の書きやすさ

● 冗長な記述は必要ない

■ 型のある言語の厳密さ

● 曖昧な記述はできない

■ 考えられたシンプルさ

● 機能を増やすことで言語を拡張していくことはしない

Goの紹介/Goの特徴 − 強力でシンプルな言語設計と文法 − 7

Goに入ってはGoに従え= 言語の思想を理解しよう

Goの特徴 − 並行プログラミング −

■ ゴールーチン

● 軽量なスレッドに近いもの● goキーワードをつけて関数呼び出し

■ チャネル

● ゴールーチン間のデータのやり取り

● 安全にデータをやり取りできる

Goの紹介/Goの特徴 − 並行プログラミング − 8

チャネルゴールーチン

Aゴールーチン

B

データ

データ

go f()

Goの特徴 − 周辺ツールの充実 −

■ go tool として標準/準標準で提供

■ サードパーティ製のツールも充実

■ IDEによらない独立したツールとして提供

Goの紹介/Goの特徴 − 周辺ツールの充実 − 9

go build ビルドを行うコマンド

go testxxxx_test.goに書かれたテスト

コードの実行

go doc, godoc ドキュメント生成

gofmt, goimports コードフォーマッター

golint コードチェッカー、リンター

gocode コード補完

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 ファイル操作など

Goの基本

● A Tour of Goをやろう

● for, if, switch● Goのインストール

● GOPATH● go tool

12

A Tour of Goをやろう

■ A Tour of Go● Goのチュートリアル

● Web上で実行できる

● Basicsにチャレンジしてみよう

Goの基本/A Tour of Goをやろう 13

繰り返し:for

■ Goの繰り返しはforのみ

Goの基本/繰り返しfor 14

// いつものforfor i := 0; i <= 100; i++ {}

// while的な使い方for i <= 100 {}

// 無限ループfor {}

()はいらない

分岐:if

■ 条件式の前に代入文などが書ける

Goの基本/分岐:if 15

// いつものifif a == 0 {}

// 代入文を書くif a := f(); a > 0 {fmt.Println(a)

} else {fmt.Println(2*a)

}()はいらない

分岐: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になる

Goのインストール

■ インストール方法

 Goの公式サイトからダウンロード

■ ソースコードからビルドする

 Goの公式サイトを参考にする。

Goの基本/Goのインストール 17

Go1.5以上はビルドにGoが必要

そのため1.4のバイナリを入れておく

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

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だけを使用)

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

手元で試してみよう!

型・メソッド・インタフェース

● 型の種類

● 配列・スライス・マップ・構造体

● type

● メソッド・インタフェース

● 埋め込み

21

型の種類型・メソッド・インタフェース/型の種類 22

組み込み型 int, float64, string など

配列[100]int など

要素の型と要素数は固定

スライス[]int など

要素の型、要素数は可変。

マップmap[string]int など

連想配列。

構造体struct { a int } などフィールドのリストを持つ

インタフェースinterface { m() int } などメソッドのリストを持つ

配列 −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で動かす

要素数が違えば別の型

...で初期値の要素に合わせる

添字と値で繰り返せる

配列 −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, 12, 23, 3

関数に渡した場合も同様に値がコピーされる

Playgroundで動かす

2つ目は省略可

スライス −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は使える

スライス −2−

スライスの背後には配列がある

型・メソッド・インタフェース/スライス −2− 26

a := [...]int{1, 2, 3, 4}b := a[1:3]fmt.Println(b, len(b), cap(b))

Playgroundで動かす

1 2 3 4a[0] a[1] a[2] a[3]

b[0] b[1]

len(b) = 2

cap(b) = 3配列

スライス

スライス −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を超えた場合は

新しく配列が確保される

スライス −4−

■ 課題1

配列とスライスをそれぞれ関数の引数や戻り値に

した場合の挙動の違いを考えてみよう。

■ 課題2

スライスへの任意位置への挿入、削除を実装して

みよう。

型・メソッド・インタフェース/スライス −4− 28

// スライスaとbを結合c := append(a, b...)

Playgroundで動かす

可変長引数にスライスを展開

マップ

map[キーの型]値の型

make(map[キーの型]値の型[, キャパシティ])

型・メソッド・インタフェース/マップ 29

a := make(map[string]int)a["c"] = 100n, 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

a := struct{N ints string

}{N: 100,s: "hoge",

}fmt.Printf("%#v\n", a)fmt.Println(a.N, a.s)

Playgroundで動かす

構造体リテラル

型情報

フィールド

typeを使った型の作成

type <型名> <型リテラル>|<既存の型>

型・メソッド・インタフェース/typeを使った型の作成 31

// 組み込み型に名前をつけるtype Int int

// 他のパッケージの型に名前をつけるtype MyWriter io.Writer

// 型リテラルに名前をつけるtype Person struct {Name string

}

intとIntは別の型として扱われる

typeで名前が付けれるもの

■ 組み込み型

int, float64, string など

■ 型リテラル構造体、インタフェース、

マップ、スライス、チャネル、関数 など

■ 名前付きの型パッケージの内外で作った型

型・メソッド・インタフェース/typeで名前を付けれるもの 32

別の型として再定義できる

メソッド −1−type で定義した型はメソッドのレシーバにできる

型・メソッド・インタフェース/メソッド −1− 33

type Hex intfunc (h Hex) String() string { return fmt.Sprintf("%x", int(h))}

// 100をHex型として代入var hex Hex = 100// Stringメソッドを呼び出すfmt.Println(hex.String())

Playgroundで動かす

メソッド −2−

■ レシーバにできるもの

● 名前の付いた型typeで定義したもの

● パッケージ内の型のみパッケージ外の型はtypeで再定義する

● ポインタ型も含むレシーバに変更を与えたい場合

レシーバも引数と同じ扱い

型・メソッド・インタフェース/メソッド −2− 34

type Hex int など

type S bufio.Scanner など

func (p *Hoge) M() {...

} など

メソッド −3−

■ 課題3

関数にメソッドを設けてみよう。var f func(string) int

■ 課題4

レシーバをポインタにしてみてレシーバに変更を与

えてみよう。構造体以外も試してみよう。

■ 課題5

レシーバがnilの場合の挙動を試してみよう。

型・メソッド・インタフェース/メソッド −3− 35

インタフェース

● メソッドのリストを持つ

● メソッドのリストがインタフェースで規定しているものと一致

する型はインタフェースを実装していることになる

型・メソッド・インタフェース/インタフェース 36

var s interface {String() string

}s = Hex(100)fmt.Println(s.String())

type Hex intfunc (h Hex) String() string { return fmt.Sprintf("%x", int(h))}

Playgroundで動かす

インタフェースを実装していることになる

型とメソッドとインタフェース

■ 既存の型にもインタフェースを実装● 後づけで実装させる

● メソッドリストさえ一致してればよい

■ 構造体以外も実装可能● typeで宣言すればメソッドが設けられる

● メソッドリストさえ一致してればよい

型・メソッド・インタフェース/型とメソッドとインタフェース 37

インタフェースの活用

■ 1つのメソッドしか持たない● io.Writer: Writeメソッド

● io.Reader: Readメソッド

■ 標準パッケージで多く使われている● fmt, net, bytes, encoding, bufio, os ...● ファイルやネットワークのコネクション

● 抽象度の高いインタフェース

型・メソッド・インタフェース/インタフェースの活用 38

インタフェースはGoの良い言語機能の1つ

インタフェース

■ 課題6

interface{} という型はどういう特徴を持つ型

か説明してください。

■ 課題7

インタフェース型を1つ作り、組み込み型、構造体

型、関数型にそれぞれ実装させてみましょう。ま

た、作ったインタフェース型を引数に取る関数を

作ってみましょう。

型・メソッド・インタフェース/インタフェース 39

型アサーション

インタフェース.(型)インタフェース型の値を任意の型にキャストする。第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

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"も試してみよう

埋め込み −1−構造体に匿名フィールドを埋め込む機能

型・メソッド・インタフェース/埋め込み −1− 42

type Hoge struct {N int

}

// Fuga型にHoge型を埋め込むtype Fuga struct {Hoge // 名前のないフィールドになる

}

埋め込み −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で動かす

埋め込みの特徴

■ 型リテラルでなければ埋め込められる● typeで定義したものや組み込み型

● インタフェースも埋め込められる

■ インタフェースの実装埋め込んだ値のメソッドもカウント

型・メソッド・インタフェース/埋め込みの特徴 44

// Stringerを実装type Hex intfunc (h Hex) String() string {

return fmt.Sprintf("%x", int(h))}// Hex2もStringerを実装type Hex2 struct {Hex}

type Stringer interface {String() string

}

Playgroundで動かす

インタフェースと埋め込み

■ 既存のインタフェースの振る舞いを変える

型・メソッド・インタフェース/インタフェースと埋め込み 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の振る舞いを変える

インタフェースと埋め込み

■ 課題8

以下のコードは有効でしょうか?Playgroundで動かして確認しましょう。

■ 課題9

前のスライドの例を実際にPlaygroundで動かして

挙動を確認しよう。

型・メソッド・インタフェース/インタフェースと埋め込み 46

type Hoge struct {N int}type Fuga struct {Hoge}f := Fuga{Hoge{100}}var _ Hoge = f _は変数を使用しないときに使う記法

HiHogeの戻り値の型がHogeにできる理由は?

参考:インタフェースの実装パターン

ゴールーチン・チャネル

● 並行と並列

● ゴールーチン

● チャネル

● チャネルを使うパターン

47

Concurrency is not Parallelism

■ 並行と並列は別ものである by RobPike● 並行:Concurrency● 並列:Parallelism

■ Concurrency● 同時にいくつかの質の異なることを扱う

■ Parallelism● 同時にいくつかの質の同じことを扱う

ゴールーチン・チャネル/Concurrency is not Parallelism 48

並列と並行の違い

■ Concurrency 同時にいくつかの質の異なることを扱う

■ Parallelism 同時にいくつかの質の同じことを扱う

ゴールーチン・チャネル/並行と並列の違い 49

本を運ぶ本を燃やす

台車を戻す

本を積む

本を燃やす 本を燃やす 本を燃やす

ゴールーチンとConcurrency

■ ゴールーチンでConcurrencyを実現● 複数のゴールーチンで同時に複数のタスクをこなす

● 各ゴールーチンに役割を与えて分業する

■ 軽量なスレッドのようなもの● LinuxやUnixのスレッドよりコストが低い

● 1つのスレッドの上で複数のゴールーチンが動く

■ ゴールーチンの作り方● goキーワードをつけて関数を呼び出す

ゴールーチン・チャネル/ゴールーチンとConcurrency 50

複数のコアで動くとは限らない

go f()

無名関数とゴールーチンゴールーチン・チャネル/無名関数とゴールーチン 51

package mainimport "fmt"import "time"func main() {

go func() {fmt.Println("別のゴールーチン")

}()fmt.Println("mainゴールーチン")time.Sleep(50*time.Millisecond)

}Sleepしないとすぐに終了する

http://play.golang.org/p/jy1HWriRTS

ゴールーチン間のデータのやりとり −1−ゴールーチン・チャネル/ゴールーチン間のデータのやりとり −1− 52

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

ゴールーチン間のデータのやりとり −2−ゴールーチン・チャネル/ゴールーチン間のデータのやりとり −2− 53

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

変数v

print(v) v = 100

共有の変数を使う?

ゴールーチン間で共有の変数を使うゴールーチン・チャネル/ゴールーチン間で共有の変数を使う 54

func main() {done := falsego func() {time.Sleep(3 * time.Second)done = true

}()for !done {time.Sleep(time.Millisecond)

}fmt.Println("done!")

}

共有の変数を使う

http://play.golang.org/p/mGSOaq4mcr

ゴールーチン間のデータ競合 −1−ゴールーチン・チャネル/ゴールーチン間のデータ競合 −1− 55

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

変数v

print(v) v = 100

処理順序が保証されない

競合

ゴールーチン間のデータ競合 −2−ゴールーチン・チャネル/ゴールーチン間のデータ競合 −2− 56

n := 1go func() {

for i := 2; i <= 5; i++ {fmt.Println(n, "*", i)n *= itime.Sleep(100)

}}()

http://play.golang.org/p/yqk82u0E4V

for i := 1; i <= 10; i++ {fmt.Println(n, "+", i)n += 1time.Sleep(100)

}

競合

データ競合の解決

■ 問題点● どのゴールーチンが先にアクセスするか分からない● 値の変更や参照が競合する

■ 解決方法● 1つの変数には1つのゴールーチンからアクセスする● チャネルを使ってゴールーチン間で通信をする● またはロックをとる(syncパッケージ)

ゴールーチン・チャネル/データ競合の解決 57

"Do not communicate by sharing memory; instead, share memory by communicating"

チャネルとは?

ゴールーチン・チャネル/チャネルとは? 58

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

ch<-100<-ch

チャネル

100

チャネルの特徴

■ 送受信できる型● チャネルを定義する際に型を指定する

■ バッファ● チャネルにバッファを持たせることができる

● 初期化時に指定できる

● 指定しないと容量0となる

■ 送受信時の処理のブロック● 送信時にチャネルのバッファが一杯だとブロック

● 受信時にチャネル内が空だとブロック

ゴールーチン・チャネル/チャネルの特徴 59

送信時のブロック

ゴールーチン・チャネル/送信時のブロック 60

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

受信してくれるまでブロック

ch<-100

チャネル

100ブロック

受信時のブロック

ゴールーチン・チャネル/受信時のブロック 61

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

送信されるまでブロック

<-ch

チャネル

100ブロック

チャネルの基本 −1−ゴールーチン・チャネル/チャネルの基本 −1− 62

■ 初期化

■ 送信

■ 受信

ch1 := make(chan int)ch2 := make(chan int, 10)

n1 := <-ch1n2 := <-ch2 + 100

容量を指定

ch1 <- 10ch2 <- 10 + 20

受け取られるまでブロック

一杯であればブロック

送信されまでブロック

空であればブロック

make(chan int, 0)と同じ

チャネルの基本 −2−ゴールーチン・チャネル/チャネルの基本 −2− 63

func main() {done := make(chan bool) // 容量0go func() {time.Sleep(time.Second * 3)done <- true

}()<-donefmt.Println("done")

}

送信されるまでブロック

http://play.golang.org/p/k0sMCYe4PA

複数のチャネルから同時に受信

ゴールーチン・チャネル/複数のチャネルから同時に受信 64

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

チャネル-1 チャネル-2

ブロック

ブロックされるので同時に送受信出来ない?

select - case −1−ゴールーチン・チャネル/select-case −1− 65

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

チャネル-1 チャネル-2

ブロックされるので同時に送受信出来ない?

select

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

nilチャネル

ゴールーチン・チャネル/nilチャネル 67

func main() {ch1 := make(chan int)var ch2 chan stringgo 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

ファーストクラスオブジェクト

■ チャネルはファーストクラスオブジェクト● 変数に入れれる

● 引数に渡す

● 戻り値で返す

● チャネルのチャネル

■ timeパッケージ

ゴールーチン・チャネル/ファーストクラスオブジェクト 68

http://golang.org/pkg/time/#After

chan chan int など

// 5分間待つ

<-time.After(5 * time.Minute)

5分たったら現在時刻が

送られてくるチャネルを返す

チャネルを引数や戻り値にする

ゴールーチン・チャネル/チャネルを引数や戻り値にする 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

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

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

受信専用のチャネル

送信専用のチャネル

タイピングゲーム

■ 課題1090秒以内に予め用意された5つの文章を入力させ、すべて入力

できた場合は"OK"と出力し、タイムオーバーの場合は"Time Over"と表示するプログラムを作ってください。なお、入力ミスし

た場合は正解するまで同じ文章を入力させてください。

ヒント:

time.After, bufio.Scanner

ゴールーチン・チャネル/タイピングゲーム 72

Concurrencyの実現

■ 複数のゴールーチンで分業する● タスクの種類によってゴールーチンを作る

● Concurrencyを実現

■ チャネルでやりとりする● ゴールーチン間はチャネルで値を共有する

● 複雑すぎる場合はロックを使うことも

■ for-selectパターン● ゴールーチンごとに無限ループを作る

● メインのゴールーチンはselectで結果を受信

ゴールーチン・チャネル/Concurrencyの実現 73

for-selectパターン −1−ゴールーチン・チャネル/for-selectパターン −1− 74

ゴールーチン-main

ゴールーチン-2

go f2()

ゴールーチン-1

go f1()

チャネル-1 チャネル-2

select

for{}for{}

各ゴールーチンで無限ループを作る

for-selectパターン −2−ゴールーチン・チャネル/for-selectパターン −2− 75

ゴルーチン-1

for{}

ゴルーチン-2

for{}

ゴルーチン-3

for{}

ゴルーチン-4

for{}

チャネル

チャネル

チャネル

チャネル

GopherでConcurrency

■ 課題11前のスライドのGopherたちが本を燃やす様子をプログラムで表

現してみてください。なお、1冊の本を燃やしたり、本を積んだ

り、台車を運んだりするのに、それなりに時間がかかることを想

定し、適度にtime.Sleepで処理を止めて見ましょう。

■ 課題12課題11で燃やす本の量をどんどん増やした場合に、どうス

ケールすれば処理速度を落とさずに本を燃やせるでしょうか?

ゴールーチン・チャネル/GopherでConcurrency 76

# データ競合のチェックgo run -race main.go

ネットワークプログラミング

● netパッケージ

● net/httpパッケージ

77

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)

}

エラー処理

エラー処理

netパッケージ −クライアント−ネットワークプログラミング/netパッケージ −クライアント− 79

p, a := "tcp", ":8080"conn, err := net.Dial(p, a)if err != nil {...}

// 書き込みfmt.Fprintln(conn, "hello")

エラー処理

手元で試してみよう!(クライアント、サーバ)

グループチャットも作ってみよう!

(プログラミング言語Goの8.10も参考になります。)

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)

手元で試してみよう!

Android上でサーバを動かす81ネットワークプログラミング/Android上でサーバを動かす

Youtubeで見る コード

母艦のシェル

adb shell

端末

タイピングゲーム2

■ 課題13課題11のタイピングゲームを改造し、netパッケージを使って

立てたtcpのサーバから問題となる文章と制限時間を取得し、そ

の時間内にタイピングできるかを競うゲームにしてみよう。

■ 課題14上記のプログラムをサーバとクライアントで実行可能ファイルを

分けずに、1つの実行ファイルでサーバもクライアントも実現しよ

う。また、問題の出題を交互に行えるようにしよう。

ネットワークプログラミング/タイピングゲーム2 82

go test とtestingパッケージ

● go test

● testingパッケージ

● コードの品質とGo

83

go testgo test と testingパッケージ/go test 84

■ testを行うためコマンド _testというサフィックスの付いた goファイルを対象にしてテストを実行

# mypkgのテスト行う$ go test mypkgok mypkg 0.007s# 失敗する場合$ go test mypkg--- FAIL: TestHex_String (0.00s)

hex_test.go:11: expect="a" actual="A"

FAILFAIL mypkg 0.008s

hex.go ⇒ hex_test.go

go testのオプション(一部)

■ -v詳細を表示する。

■ -cpu実行する並列度を指定する。複数のコアを使ったテストができる。

■ -raceデータの競合が起きないかテストする。

■ -coverカバレッジを取得する。

go test と testingパッケージ/go testのオプション(一部) 85

testingパッケージ

■ テストを行うため機能を提供するパッケージ *testing.T型のメソッド使う。

go test と testingパッケージ/testingパッケージ 86

package mypkg_testimport "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 intfunc (h Hex) String() string {

return fmt.Sprintf("%x", int(h))}

mypkg.go

mypkg_test.go

testingパッケージでできること

■ 失敗理由を出力してテストを失敗させる Error(), Errorf(), Fatal(), Fatalf()

■ テストの並列実行Parallel()go testの-parallelオプションで並列数を指定

■ ベンチマーク *testing.B型を使う

■ ブラックボックステストtesting/quickパッケージ

go test と testingパッケージ/testingパッケージでできること 87

testingパッケージでできないこと

■ アサーションはない自動でエラーメッセージを作るのではなく、

ひとつずつErrorfを使って自前で作る

■ テストはGoで書くテストのための新しいミニ言語を作らない

■ 比較演算子だけはツライのでは?reflect.DeepEqualを使うと良い

go test と testingパッケージ/testingパッケージでできないこと 88

かなり薄いテストパッケージ

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

Goのコンセプト

■ 実現する手段は少ないほうが良い 機能が増えると複雑さが増える。

■ 暗黙的で曖昧な記述をさせない エラーにつながる,暗黙の型変換や

 不使用の変数等の宣言は許さない。

■ 不要なものは避ける 不要な型の宣言の排除など,

 タイプ数をできるだけ減らすように。

■ コンセプトに一貫性を持たせる コンセプトにずれる言語仕様は入れない。

go test と testingパッケージ/Goのコンセプト 90

“Simplicity is Complicated” by Rob Pike

シンプルさと強力さ

■ シンプルな機能を組み合わせる シンプルな機能を組み合わせて,複雑な問題に対処する

  ⇒シンプルだが強力さも十分ある

go test と testingパッケージ/シンプルさと強力さ 91

シンプル簡潔さ

強力さ表現力

品質の良いコードが作りやすい

Goのコード品質を高める要素

■ シンプルな文法と言語設計 可読性の高い文法と複雑になりにくい言語仕様。

■ 型階層がない 複雑な型の階層が存在せず不要な型ができにくい。

■ コンパイルによるエラー検出 静的型付け言語なのでバグがコンパイル時に分かる。 バグになり得る箇所がコンパイルエラーになる。 (型不一致,未使用の変数など)

■ コードフォーマッタ 標準のコードフォーマッタ(gofmt)がある。

■ テスト 標準のテストツール(go test)がある

go test と testingパッケージ/Goのコード品質を高める要素 92

コンパイルエラーになるもの −1−

■ 型の不一致

go test と testingパッケージ/コンパイルエラーになるもの −1− 93

var n int = 100var m float64 = 1.5

// エラーvar a int = n + m

// OKvar b int = n + int(m)

コンパイルエラーになるもの −2−

■ 未使用の変数/パッケージ

go test と testingパッケージ/コンパイルエラーになるもの −2− 94

import ("fmt" // エラー_ "io" // OK

)

func main() {var n int = 100 // エラー_ = 200 // OK

}

コンパイルエラーになるもの −3−

■ インタフェースの未実装(型の不一致)

go test と testingパッケージ/コンパイルエラーになるもの −3− 95

type Hex intfunc (h Hex) Str() string {

return fmt.Sprintf("%x", int(h))}

// エラーvar _ fmt.Stringer = Hex(100)

type Stringer interface {

String() string}

コンパイルエラーになるもの −4−

■ 曖昧な記述

go test と testingパッケージ/コンパイルエラーになるもの −4− 96

type Hoge struct{ N int }type Piyo struct{ N int }type Foo struct {

HogePiyo

}func main() {

f := Foo{Hoge{100}, Piyo{200}}fmt.Println(f.N) // エラーfmt.Println(f.Hoge.N) // OK

}

Goとテスト

■ 言語のコンセプトを守る● 実現する手段は少なく

⇒ テストの為のミニ言語を入れない。

● 暗黙的で曖昧な記述をさせない

⇒ アサーションで自動でエラーメッセージを作らない。

 コンテキストにあったエラーメッセージを作る。

■ コンパイルエラーで検出できるコンパイルで検出できるものはテストは不要。

コンパイルでは検出できないものに集中できる。

■ テストが良いサンプルテストがGoで書かれてるので良いサンプルになる。

go test と testingパッケージ/Goとテスト 97

FAQを読もう!

ドキュメントとテスト

■ テストされたサンプル

func ExampleHex_String() {fmt.Println(mypkg.Hex(10))// Output: a

}

テストファイルにExampleで始まる関数を書くとサンプルとして扱われる。

// Output:を書くとテスト対象になる。

go test と testingパッケージ/ドキュメントとテスト 98

言語標準ツールの強み

■ ツール間で連携が取りやすい標準ツールなので、他のツールと連携が取りやすい

■ メンテが保証される バグが放置されたり、メンテされなかったりしない

■ みんなが共通に使うその言語を使っているユーザ間で、共通の知識になる

テストツールも言語標準のメリットは大きい

go test と testingパッケージ/言語標準ツールの強み 99

課題

■ 課題15hogeパッケージのテスト書いてみましょう。

■ 課題16Exapleテストを書いてみましょう。

また、godocを使ってドキュメント生成してみましょう。

go test と testingパッケージ/課題 100

# godocをインストールしよう$ go get golang.org/x/tools/cmd/godoc$ $GOPATH/bin/godoc --http=":8080"

リフレクション

● reflectパッケージとは

● リフレクションの基本

101

reflectパッケージとは?

■ 何ができるのか?

● 実行時に型情報を取得

● 任意の型の変数に値を入れる

● 構造体のフィールドのタグを取得する

■ どこで使われてるの?● encodingパッケージ● ORマッパーなど

リフレクション/reflectパッケージとは? 102

encodingパッケージでの利用

● JSONなどのシリアライズされたものを構造体に変換する際に使用される

type Person struct {Name string `json:"name"`Aget int `json:"age"`

}

{"name": "Gopher","age": 4

}

Goの構造体:

JSON例:

structタグで対応付ける

リフレクション/encodingパッケージでの利用

templateパッケージでの利用

● HTMLなどに任意の型の値を埋め込むために使われる

Hello, {{.Name}}!! Hello, Gopher!!

Person {Name: “Gopher”,Age: 4,

}

テンプレート 出力

データの埋込み

Execute

リフレクション/templateパッケージでの利用

Value型とType型

■ Value型

● 任意の値を表す型

● 値への操作をメソッドで提供

● reflect.ValueOf()で取得できる

■ Type型● 任意の型を表す型● 型に関する操作をメソッドで提供

● reflect.TypeOf()で取得できる

105リフレクション/Value型とType型

変数に値を入れる106

var n intfmt.Println(n) // 0

vp := reflect.ValueOf(&n)v := vp.Elem()if v.CanSet() {v.SetInt(100)

}

fmt.Println(n) // 100http://play.golang.org/p/HkJPjQsP8o

リフレクション/変数に値を入れる

変数に値を入れる(図解)

100n: &n

Value(ポインタ)

ValueOf()

Value(Int)

Elem()

Set

リフレクション/変数に値を入れる(図解)

interface{}としてポインタを渡す

■ interface{}として、任意の型のポインタを受け取る

func set(p, v interface{}) {pv := reflect.ValueOf(p) // ポインタ

vv := reflect.ValueOf(v) // 設定する値

pv.Elem().Set(vv)}

リフレクション/interface{}としてポインタを渡す

構造体のリフレクション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

リフレクション/構造体のリフレクション

リフレクションの注意点

■ 容易にpanicが起きる

● ValueとTypeで実体によって対応していないメソッドを呼ぶとpanicになる○ 例:int型の値のValueに対してLen()を呼ぶ

● Kindで適切に分岐してpanicを避ける

■ 実行時間がかかる

● 実行時に解析するのでコストが大きい● 静的解析を用いる選択肢もある

リフレクション/リフレクションの注意点

静的解析

● 静的解析とは?

● Goにおける静的解析

● 静的解析の基本

111

ソースコードの静的解析とは?112

■ ソースコードを実行せずに解析すること

● ソースコードから抽象構文木(AST)などを取得して解析する

● 静的型付け言語だと、静的解析で型情報が取得できる

● 逆は実行して解析する動的解析

静的解析/ソースコードの静的解析とは?

Goで静的解析をすると何が嬉しいのか?

● リファクタリングツール○ 変数の宣言位置や使用箇所の抽出○ パッケージの解析

● コードジェネレーター○ コメントによるアノテーションの抽出○ コードフォーマッタ

● 処理系○ 抽象構文木(AST)の解析○ 定数の扱い

113

静的型付け言語なので静的解析でも多くの事が知れる

静的解析/Goで静的解析をすると何が嬉しいのか?

開発ツールとソースコードの静的解析114

■ 開発ツールの多くは静的解析を行っている● gofmt/goimports

○ コードフォーマッター● go vet/golint

○ コードチェッカー、リンター● guru

○ 静的解析

● gocode○ コード補完

● errcheck○ エラー処理のチェック

● gorename/gomvpkg○ リファクタリングツール

静的解析/開発ツールとソースコードの静的解析

■ 標準パッケージで静的解析の機能を提供

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

ソースコード

トークン

抽象構文木(AST)

型情報

構文解析

字句解析

型チェック

go/scannergo/token

go/parsergo/ast

go/typesgo/constant

静的解析/静的解析の流れ

字句解析 - go/scanner,go/token

■ 文字列をトークンにしていく

● 空白などを取り除き、意味のある単位=トークンにしていく作業

117

v + 1

IDENT ADD INT

トークン

ソースコード

静的解析/字句解析

構文解析 - go/parser,go/ast

■ トークンを抽象構文木(AST)にしていく

● プログラムの構造を持たせる

118

v + 1

IDENT ADD INT

ソースコード

+

v 1

BinaryExpr

Ident BasicLit

トークン

抽象構文木(AST)

静的解析/構文解析

型チェック - go/types,go/constant

■ 型チェックを行う

● 識別子の解決● 型の推論● 定数の評価

119

n := 100 + 200

fmt.Println(n)

定数の評価=300

型の推論-> int

識別子の解決

識別子の解決-> fmtパッケージ

静的解析/型チェック

抽象構文木(ASt)の取得

■ go/parserパッケージの関数を使う

● ParseExpr,ParseExprFrom○ 式をパースする○ ParseExprはParseExprFromを簡易版

● ParseFile○ ファイル単位でパースする

● ParseDir○ ディレクトリ単位でパースする○ 中でParseFileを呼んでいる

120静的解析/抽象構文木(AST)の取得

式の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を取得する

token.FileSetとは?

■ ファイル中の位置情報を記録する為の型

● 位置情報は数値で表される● 複数のファイル間で一意の値● 各ファイルのoffsetが記録されている● パースする際に記録されていく

122

token.FileSetは出力引数としてParse系の関数に渡す

静的解析/token.FileSetとは?

ファイルからASTを取得する

■ 完全なGoのソースコードを構文解析する

123

const src = `package mainvar v = 100func 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を取得する

Hello, WorldのASTの構成124

package mainimport "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の構成

ASTをトラバースする

■ ast.Inspectを使う

125

n, _ := parser.ParseExpr(`v + 1`)ast.Inspect(n, func(n ast.Node) bool {

if n != nil { fmt.Printf("%T\n", n) }return true

})printer.Fprint(os.Stdout, token.NewFileSet(), n)

*ast.BinaryExpr*ast.Ident*ast.BasicLitv + 1

+

v 1

構文解析

抽象構文木(AST)を探索

抽象構文木(AST)を出力

BinaryExpr

Ident BasicLit

Playgroundで動かす

ast.Walkというのもある

静的解析/ASTをトラバースする

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をトラバースする

参考資料

■ goパッケージで簡単に静的解析して世界を広げよう ● コードジェネレータ

○ ASTを取得する方法を調べる○ 抽象構文木(AST)をトラバースする○ 抽象構文木(AST)をいじってフォーマットをかける○ Goの抽象構文木(AST)を手入力してHello, Worldを作る○ go-app-builderのソースコードを読む

● リファクタリングツール○ gorenameをライブラリとして使う○ Goのスコープについて考えてみよう○ go/typesパッケージを使い変数名をリネームしてみる

● 処理系○ 簡単な式の評価機を作ってみる○ 【実践goパッケージ】文字列から複素数型の値をパースする○ もっと楽して式の評価器を作る

127静的解析/参考資料

ハンズオン

● ハンズオンの説明

● ドキュメントとPlayground

128

ハンズオンの説明

■ リポジトリ● https://github.com/tenntenn/gohandson/tree/master/i

mgconv/ja

■ コマンドラインツール

● ターミナルで動くプログラム● 画像を変換するツール

# 50%に縮小して、JPEGにする$ imgconv -resize 50% a.png b.jpg

ハンズオン/ハンズオンの説明 129

ドキュメントを読もう

■ パッケージドキュメント● https://golang.org/pkg● 標準パッケージの使い方が書いてある

■ FAQ● https://golang.org/doc/faq● なぜGoに◯◯がないのか?など

■ 言語仕様

● https://golang.org/ref/spec公式ドキュメントを読もう!!

ハンズオン/ドキュメントを読もう 130

Go Playground

■ Go Playground● http://play.golang.org/● Web上でGoを実行できる● Share機能で、SNSで共有したり質問する

ハンズオン/Go Playground 131