はじめに
前回の続きです。
やってみた
制御構文
if文
if の条件式の中で変数定義ができ、その変数のスコープはif文の中のみ有効になる
func main() { if aaa := "test"; aaa == "test" { fmt.Println("OK => " + aaa) } else { fmt.Println("NG => " + aaa) } // ここではaaaは使えない // fmt.Println(aaa) } // OK => test
switch文
switch文の各caseでbreakは必要ない。
func main() { var aaa string = "午後" switch aaa { case "午前": fmt.Println("おはよう") case "午後": fmt.Println("こんにちは") case "夜": fmt.Println("こんばんは") } } // こんにちは
if文と同じような書き方もできる
func main() { switch aaa := "午後"; aaa { case "午前": fmt.Println("おはよう") case "午後": fmt.Println("こんにちは") case "夜": fmt.Println("こんばんは") } // ここではaaaは使えない // fmt.Println(aaa) } // こんにちは
こんな書き方も可能
func main() { var aaa string = "午後" switch { case aaa == "午前": fmt.Println("おはよう") case aaa == "午後": fmt.Println("こんにちは") case aaa == "夜": fmt.Println("こんばんは") } } // こんにちは
関数
複数の戻り値が設定できる
func main() { var price, discountPrice = GetPrice(20) fmt.Println("価格 => " + strconv.Itoa(price)) fmt.Println("割引価格 => " + strconv.Itoa(discountPrice)) } func GetPrice(discount int) (int, int) { var price int = 100 return price, price - discount } // 価格 => 100 // 割引価格 => 80
戻り値に名前をつけられる
func main() { var normalPrice, discountPrice = GetPrice(100) fmt.Println("通常価格 => " + strconv.Itoa(normalPrice)) fmt.Println("割引価格 => " + strconv.Itoa(discountPrice)) } func GetPrice(discount int) (normalPrice int, discountPrice int) { // 戻り値の名前をしていると、それをそのまま関数内で使用できて、returnに値をセットする必要がなくなる normalPrice = 500 discountPrice = normalPrice - discount return } // 通常価格 => 500 // 割引価格 => 400
関数を変数に代入できる。型を指定しなければいけないところがなるほどと思った。
func main() { var priceFunc func(int) (int, int) priceFunc = GetPrice fmt.Println(priceFunc(200)) } func GetPrice(discount int) (normalPrice int, discountPrice int) { normalPrice = 500 discountPrice = normalPrice - discount return } // 500 300
defer文
defer で指定した処理が、関数の終了時に実行されるようになる。
__destruct()みたいな感じかもしれない。
func main() { Story() } func Story() { defer fmt.Println("おしまい") fmt.Println("昔々あるところにおじいさんとおばあさんがいました") fmt.Println("おじいさんは山へリオレウスを狩りに") fmt.Println("おばあさんは川へフルフルを狩りにでかけました") } // 昔々あるところにおじいさんとおばあさんがいました // おじいさんは山へリオレウスを狩りに // おばあさんは川へフルフルを狩りにでかけました // おしまい
パニック
処理を停止させる。関数?
スタックトレースが表示される。
func main() { Story() } func Story() { fmt.Println("昔々あるところにおじいさんとおばあさんがいました") fmt.Println("おじいさんは山へリオレウスを狩りに") panic("パニック!モンハンと桃太郎の世界戦が一緒だ!") // ↑でpanicを呼び出しているので、このPrintは呼び出されない fmt.Println("おばあさんは川へフルフルを狩りにでかけました") } // 昔々あるところにおじいさんとおばあさんがいました // おじいさんは山へリオレウスを狩りに // panic: パニック!モンハンと桃太郎の世界戦が一緒だ! // goroutine 1 [running]: // main.Story() // /xxx/xxx/study_02/main.go:72 +0xbd // main.main() // /xxx/xxx/study_02/main.go:63 + 0x20 // exit status 2
ちなみにdefer
をつけると、deferを呼び出した後、panicへ流れるようだった。
func main() { Story() } func Story() { defer fmt.Println("おしまい") fmt.Println("昔々あるところにおじいさんとおばあさんがいました") fmt.Println("おじいさんは山へリオレウスを狩りに") panic("パニック!モンハンと桃太郎の世界戦が一緒だ!") // ↑でpanicを呼び出しているので、このPrintは呼び出されない fmt.Println("おばあさんは川へフルフルを狩りにでかけました") } // 昔々あるところにおじいさんとおばあさんがいました // おじいさんは山へリオレウスを狩りに // おしまい // panic: パニック!モンハンと桃太郎の世界戦が一緒だ! // goroutine 1 [running]: // main.Story() // /xxx/xxx/study_02/main.go:72 +0x119 // main.main() // /xxx/xxx/study_02/main.go:63 +0x20 // exit status 2
メソッド
前回の続き でも使用したけど。もう一回おさらい
ポインタ部分がすぐ変換できない。。。
type Item struct { name string price int stock int } func (item Item) GetName() string { return item.name } func (item Item) GetPrice() int { return item.price } func (item Item) GetStock() int { return item.stock } func (item *Item) Reduce(count int) { item.stock -= count } func main() { var item = Item{ name: "黄金の林檎", price: 500, stock: 5, } item.Reduce(2) item.Reduce(1) fmt.Println("アイテム名: " + item.GetName()) fmt.Println("金額: " + strconv.Itoa(item.GetPrice())) fmt.Println("在庫: " + strconv.Itoa(item.GetStock())) } // アイテム名: 黄金の林檎 // 金額: 500 // 在庫: 2
インターフェース
こんな形かな? もうちょっと規模が大きいものを書いて、その時に使用してみたい。
type Car interface { EngineStart() } type MyCar struct { started bool } func (myCar MyCar) EngineStart() { myCar.started = true fmt.Println("エンジン点火!ブルルルーン!") } func main() { var myCar Car = MyCar{ started: false, } myCar.EngineStart() } // エンジン点火!ブルルルーン!
エラーハンドリング
Go言語例外処理がないらしいが、Errorインターフェースというのがあるみたい。
用意されてる標準関数とかも、エラー時はErrorインターフェースを返すみたいだ。
自作の関数とかでErrorを返却したい場合は、errorsパッケージを読み込んで New関数を使うよう。
import ( "errors" "fmt" "strconv" ) type Item struct { stock int } func (item Item) GetStock() int { return item.stock } func (item *Item) Reduce(count int) error { item.stock -= count if item.stock <= 0 { return errors.New("在庫はもうない!") } return nil } func main() { var item = Item{ stock: 5, } for i := 0; i < 5; i++ { fmt.Println(strconv.Itoa(i+1) + "回目の購入!") e := item.Reduce(2) if e != nil { fmt.Println(e.Error()) break } fmt.Println("在庫を2つ消費した!現在の在庫は" + strconv.Itoa(item.GetStock())) } } // 1回目の購入! // 在庫を2つ消費した!現在の在庫は3 // 2回目の購入! // 在庫を2つ消費した!現在の在庫は1 // 3回目の購入! // 在庫はもうない!
ゴルーチン
並列処理を行える。
ゴルーチンで呼び出した関数の終了を待たないので、戻り値は受け取れない。
mainから呼び出した場合は、mainの処理が終了した時点で、呼び出したゴルーチンも途中で処理を中断して終了する。
import ( "fmt" "time" ) func main() { CountUp() go CountUp() time.Sleep(2 * time.Second) fmt.Println("Count Up終わり!") } func CountUp() { defer fmt.Println("おしまい") for count := 0; count < 5; count++ { fmt.Println(count) time.Sleep(1 * time.Second) } } // 0 // 1 // 2 // 3 // 4 // おしまい // 0 // 1 // 2 // Count Up終わり!
チャネル
ゴルーチン間で値の送受信を行うために使う。
import ( "fmt" "time" ) func main() { var ch = make(chan string) go CountUp("Goルーチン1号君", ch) go CountUp("Goルーチン2号君", ch) fmt.Println(<-ch) fmt.Println(<-ch) close(ch) } func CountUp(title string, ch chan<- string) { fmt.Println(title + " 起床") time.Sleep(300 * time.Millisecond) fmt.Println(title + " 朝食") time.Sleep(300 * time.Millisecond) fmt.Println(title + " 通勤") time.Sleep(300 * time.Millisecond) fmt.Println(title + " 勤務") time.Sleep(300 * time.Millisecond) fmt.Println(title + " 帰宅") ch <- title + " 就寝。今日も一日お疲れ様" } // Goルーチン2号君 起床 // Goルーチン1号君 起床 // Goルーチン2号君 朝食 // Goルーチン1号君 朝食 // Goルーチン1号君 通勤 // Goルーチン2号君 通勤 // Goルーチン1号君 勤務 // Goルーチン2号君 勤務 // Goルーチン1号君 帰宅 // Goルーチン1号君 就寝。今日も一日お疲れ様 // Goルーチン2号君 帰宅 // Goルーチン2号君 就寝。今日も一日お疲れ様
とりあえずサンプル書いてみたけど、チャネルはあまり理解できてない。。
すごく単純なプログラムだからだと思うけど、この動きを見ると、goルーチンが終わるまで待っている感じ。
もう少し深く勉強しなければ。
おわりに
前回書いた後いろいろと調べると、変数も関数もUpperCamelCaseで書いている記事が複数あったのですが、関数は UpperCamelCase, 変数は LowerCamelCaseと分けている記事もありました。
参考にしている資料は、以下だったので、とりあえずこっちに合わせておこうと思います。
関数は UpperCamelCase, 変数は LowerCamelCase
もうちょい基礎頑張って次はフレームワークも触ってみます。
あ、10/8は技術書典行きます!