#contents ** Go言語とはなにか? [#d30d648d] - 概要 -- Google -- シンプルな言語仕様 -- 高速 -- 並行プログラミング - 公式サイト -- https://golang.org/ - 前提知識 -- Unixコマンド -- C言語 - 環境 -- CentOS7 (Vagrant) -- sudo yum install golang - Goインストール $ pwd /home/vagrant/golang_lessons $ sudo yum -y install golang $ go version go version go1.6.3 linux/amd64 ** はじめてのGoプログラム [#p18d123b] - hello.go package main import "fmt" func main() { fmt.Println("hello world") } - コンパイル $ go build hello.go $ ls hello hello.go - 実行 $ ./hello hello world - コンパイル&実行 $ go run hello.go hello world - 言語の特徴 -- 1行コメントは「//」複数行コメントは「/* */」 -- 文末に「;」は不要 -- インデントはスペースではなくタブ推奨 ** 変数を使ってみよう [#vd8a2dfc] - 基本形 func main() { var msg string msg = "hello world" fmt.Println(msg) } - 変数宣言と同時に値代入 &color(red){※}; 型 (string) は省略可能 func main() { var msg = "hello world" fmt.Println(msg) } - 変数宣言と同時に値代入 &color(red){※}; := を利用した記法 func main() { msg := "hello world" fmt.Println(msg) } - 実行結果 $ go run hello.go hello world - 複数変数を同時に宣言 func main() { // var a, b int // a, b = 10, 15 a, b := 10, 15 } - 型が異なる変数を同時に宣言 func main() { var ( c int d string ) c = 20 d = "hoge" } - 変数名の規則 -- 1文字目が小文字: そのパッケージ内からのみアクセス可 -- 1文字目が大文字: 他のパッケージからもアクセス可 -- 変数、定数、関数についても同じルール ** 基本データ型を使ってみよう [#n775ca6b] - 文字列: string -- "hello" -- 初期値: 空文字 "" &color(red){※}; nil では無い - 整数: int -- 53 -- 初期値: 0 - 浮動小数: float64 -- 10.2 - 真偽値: bool -- false / true -- 初期値: false - nil - printf の使い方 package main import "fmt" func main() { a := 10 b := 12.3 c := "hoge" var d bool fmt.Printf("a:%d, b:%f, c:%s, d:%t\n", a, b, c, d) } - 実行結果 $ go run hello.go a:10, b:12.300000, c:hoge, d:false ** 定数を使ってみよう [#l78900cb] - const で宣言 - 値の変更不可 - hello.go package main import "fmt" func main() { const name = "taguchi" name = "fkoji" fmt.Println(name) } - 実行結果 $ go run hello.go # command-line-arguments ./hello.go:7: cannot assign to name - iota を使った定数宣言 &color(red){※}; 自動で連番の値を付与 package main import "fmt" func main() { const ( sun = iota // 0 mon // 1 tue // 2 ) fmt.Println(sun, mon, tue) } - 実行結果 $ go run hello.go 0 1 2 ** 基本的な演算をしてみよう [#me90869c] - 四則演算: + - * / % - 文字列: + - 論理値: AND(&&) OR(||) NOT(!) - 余りの計算 func main() { var x int x = 10 % 3 fmt.Println(x) } - 実行結果 $ go run hello.go 1 - x = x + 3 の代入 func main() { var x int x += 3 // x = x + 3 fmt.Println(x) } - 実行結果 $ go run hello.go 3 - インクリメント &color(red){※}; "x = x++" や "++x" の記法はGoでは使えない func main() { var x int x++ fmt.Println(x) } - 実行結果 $ go run hello.go 1 - 文字列の連結 func main() { var s string s = "hello " + "world" fmt.Println(s) } - 実行結果 $ go run hello.go hello world - 論理値の演算 func main() { a := true b := false fmt.Println(a && b) fmt.Println(a || b) fmt.Println(!a) } - 実行結果 $ go run hello.go false true false ** ポインタを使ってみよう [#fe70ae83] - ポインタ -- メモリ上のアドレスを指し示す変数 -- 演算はできない - hello.go package main import "fmt" func main() { a := 5 var pa *int pa = &a // &a = aのアドレス // paの領域にあるデータの値 = *pa fmt.Println(pa) fmt.Println(*pa) } - 実行結果 $ go run hello.go 0xc82000a2a0 5 ** 関数を使ってみよう [#aa05ab6e] - 基本形 (引数なし) func hi() { fmt.Println("hi!") } func main() { hi() } - 実行結果 $ go run hello.go hi! - 基本形 (引数あり) func hi(name string) { fmt.Println("hi!" + name) } func main() { hi("yuji") } - 実行結果 $ go run hello.go hi!yuji - 返り値を指定 func hi(name string) string { // 返り値の型を指定 msg := "hi!" + name return msg } func main() { fmt.Println(hi("yuji")) } - 実行結果 $ go run hello.go hi!yuji - 返り値に変数名 (msg) を指定 &color(red){※}; return 時に変数名指定が不要 func hi(name string) (msg string) { msg = "hi!" + name return } func main() { fmt.Println(hi("yuji")) } - 実行結果 $ go run hello.go hi!yuji - 返り値を2つ指定 &color(red){※}; Go言語の特徴として返り値を2つ指定することが可能 func swap(a, b int) (int, int) { return b, a } func main() { fmt.Println(swap(5, 2)) } - 関数を変数に代入 func main() { f := func(a, b int) (int, int) { // 関数名は省略可 return b, a } fmt.Println(f(2, 3)) } - 実行結果 $ go run hello.go 3 2 - 即時関数 func main() { func(msg string) { fmt.Println(msg) }("yuji") } - 実行結果 $ go run hello.go yuji ** 配列を使ってみよう [#c7615a8d] - 基本形 func main() { var a [5]int // a[0] - a[4] a[2] = 3 a[4] = 10 fmt.Println(a) fmt.Println(a[2]) } - 実行結果 $ go run hello.go [0 0 3 0 10] 3 - 配列の宣言と値の代入を同時に実施 func main() { // b := [3]int{1, 3, 5} b := [...]int{1, 3, 5} // 配列の個数は自明のため省略 (...) 可 fmt.Println(b) fmt.Println(len(b)) // 配列の要素の個数を取得 } - 実行結果 $ go run hello.go [1 3 5] 3 ** スライスを使ってみよう [#n8d5f661] - 配列 -- 関数に配列を渡す場合には値を丸ごと渡すためメモリ的に非効率 -- 配列の要素数は固定になっていて動的に変化させることができない - スライス -- 配列を便利に使えるようにしたデータ型 -- 配列の一部または全部を指し示す参照型のデータ -- Go言語においては配列よりもスライスがよく使われる - 配列の一部をスライスで参照 func main() { a := [5]int{2, 10, 8, 15, 4} s := a[2:4] // [8, 15] ... 配列 a の添字は省略可 [:] [:4] [2:] 等 fmt.Println(a) fmt.Println(s) } - 実行結果 $ go run hello.go [2 10 8 15 4] [8 15] - スライスの値を変更 &color(red){※}; 参照先の配列の値も変更されることに注意 func main() { a := [5]int{2, 10, 8, 15, 4} s := a[2:4] // [8, 15] s[1] = 12 fmt.Println(a) fmt.Println(s) } - 実行結果 $ go run hello.go [2 10 8 12 4] [8 12] - len (要素数), cap (スライスの最大容量: スライスの最初の位置から切り出せる最大個数) func main() { a := [5]int{2, 10, 8, 15, 4} s := a[2:4] // [8, 15] s[1] = 12 fmt.Println(len(s)) fmt.Println(cap(s)) } - 実行結果 $ go run hello.go 2 3 ** make()、append()、copy()を使おう [#qe032704] - make を使ってスライスを宣言 (初期値は0) func main() { s := make([]int, 3) // [0 0 0] fmt.Println(s) } - make を使ってスライスを宣言 (初期値を指定) func main() { s := []int{1, 3, 5} fmt.Println(s) } - 実行結果 $ go run hello.go [1 3 5] - append を使ってスライスの末尾に要素を追加 func main() { s := []int{1, 3, 5} // append s = append(s, 8, 2, 10) fmt.Println(s) } - 実行結果 $ go run hello.go [1 3 5 8 2 10] - copy を使ってスライス s をスライス t にコピー &color(red){※}; copy() の返り値はコピーした要素の数 func main() { s := []int{1, 3, 5} // append s = append(s, 8, 2, 10) // copy t := make([]int, len(s)) n := copy(t, s) fmt.Println(s) fmt.Println(t) fmt.Println(n) } - 実行結果 $ go run hello.go [1 3 5 8 2 10] [1 3 5 8 2 10] 6 ** マップを使ってみよう [#m6f26d89] - 添字にキーを使ってキーと値のペアで管理可能なデータ型 - 他の言語では ハッシュ や 連想配列 とも呼ばれている - 基本形 func main() { m := make(map[string]int) // key: string型, value: int型 m["yuji"] = 200 m["shimojo"] = 300 fmt.Println(m) } - 実行結果 $ go run hello.go map[yuji:200 shimojo:300] - マップの宣言と key / value の代入を同時に実施 func main() { m := map[string]int{"yuji":100, "shimojo":200} fmt.Println(m) } - 実行結果 $ go run hello.go map[shimojo:200 yuji:100] - len (key/valueペアの要素数), delete (ペアの削除) func main() { m := map[string]int{"yuji":100, "shimojo":200} fmt.Println(m) fmt.Println(len(m)) delete(m, "yuji") fmt.Println(m) } - 実行結果 $ go run hello.go map[yuji:100 shimojo:200] 2 map[shimojo:200] - マップに特定のキーが存在するか確認 func main() { m := map[string]int{"yuji":100, "shimojo":200} v, ok := m["shimojo"] fmt.Println(v) fmt.Println(ok) } - 実行結果 $ go run hello.go 200 true ** ifで条件分岐をしてみよう [#y54f0b66] - 基本形 func main() { score := 83 if score > 80 { fmt.Println("Great!") } else if score > 60 { fmt.Println("Good!") } else { fmt.Println("so so...") } } - 実行結果 $ go run hello.go Great! - if 文の中で変数を定義 func main() { if score := 43; score > 80 { fmt.Println("Great!") } else if score > 60 { fmt.Println("Good!") } else { fmt.Println("so so...") } // fmt.Println(score) // score は if 文の中のスコープのため上記は undefined エラーとなる } - 実行結果 $ go run hello.go so so... - if 文 の条件で利用可能な演算子 -- 比較演算子: > >= < <= == != -- 論理演算子: && || ! ** switchで条件分岐をしてみよう [#pc429ac3] - 基本形 func main() { signal := "blue" switch signal { case "red": fmt.Println("Stop") case "yellow": fmt.Println("Caution") case "green", "blue": fmt.Println("Go") default: fmt.Println("wrong signal") } } - 実行結果 $ go run hello.go Go - if else 文を switch 文で置き換え func main() { score := 82 switch { case score > 80: fmt.Println("Great!") default: fmt.Println("so so ...") } } - 実行結果 $ go run hello.go Great! ** forでループ処理をしてみよう [#e58240f0] - Go言語では繰り返し処理は for 文のみ (while 文は存在しない) - 基本形 func main() { for i := 0; i < 10; i++ { // if i == 3 { break } // 0, 1, 2 if i == 3 { continue } // 0, 1, 4, 5, 6, 7, 8, 9 fmt.Println(i) } } - 実行結果 $ go run hello.go 0 1 2 4 5 6 7 8 9 - while 文風な記法 func main() { i := 0 for i < 10 { fmt.Println(i) i++ } } - 実行結果 $ go run hello.go 0 1 2 3 4 5 6 7 8 9 - 条件を省略 &color(red){※}; 無限ループとなるため break 必須 func main() { i := 0 for { fmt.Println(i) i++ if i == 3 { break } } } - 実行結果 $ go run hello.go 0 1 2