教材
学んだ事
値渡しについて
Goでは関数への変数受け渡しは値渡しとなる。参照渡しをしたい場合はポインタを利用する。
package main func main() { firstName := "john" updateName(firstName) println(firstName) // ポインタ: 別の変数のメモリアドレスを格納する変数 // &演算子: &の後にあるオブジェクトのメモリアドレスを取得 // &firstName: firstNameのメモリアドレスを取得 // firstNameが格納されたメモリアドレスを渡す updateNamePointer(&firstName) println(firstName) } // Goでは値渡しを行う func updateName(name string) { name = "david" } // 意図的に参照渡しを行う // ここでいうとnameがポインタ、*nameでポインタを逆参照する // *演算子: ポインタに格納されたメモリアドレスにあるオブジェクトへのアクセスを付与 // ここでいうとfirstName変数のメモリアドレスに対してname変数で触れるようにしている // メモリアドレスに格納された変数の型で受け取る func updateNamePointer(name *string) { *name = "123" // すでにstring型でメモリを確保しているのでint型して突っ込めない //cannot assign int to *name (type string) in multiple assignment //*name, _ = strconv.Atoi(*name) }
switch~caseの使い方
https://docs.microsoft.com/ja-jp/learn/modules/go-control-flow/4-use-defer-statement
通常の使い方、他の言語でもこんな感じ。
switch i { case 0: fmt.Print("zero...") case 1: fmt.Print("one...") case 2: fmt.Print("two...") default: fmt.Print("no match...") }
switchで関数呼び出し&caseで複数条件
複数の条件に対しても使える&switchで関数呼び出し可能
switch time.Now().Weekday().String() { case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday": fmt.Println("It's time to learn some Go.") default: fmt.Println("It's weekend, time to rest!") }
caseで関数呼び出し可能
var email = regexp.MustCompile(`^[^@]+@[^@.]+\.[^@.]+`) var phone = regexp.MustCompile(`^[(]?[0-9][0-9][0-9][). \-]*[0-9][0-9][0-9][.\-]?[0-9][0-9][0-9][0-9]`) contact := "foo@bar.com" switch { case email.MatchString(contact): fmt.Println(contact, "is an email") case phone.MatchString(contact): fmt.Println(contact, "is a phone number") default: fmt.Println(contact, "is not recognized") }
フォールスルー(fall through)
通常は1つのcaseに合致すればそこで処理が終了する。
i := 0 switch { case i == 0: fmt.Println("zero") case i == 1: fmt.Println("one") case i == 2: fmt.Println("two") default: fmt.Println("no match") }
$ go run statement.go zero
しかしこんな場合はどうだろうか。
switch { case i == 0: fmt.Println("zero") case i == 0: fmt.Println("zero2") case i == 1: fmt.Println("one") case i == 2: fmt.Println("two") default: fmt.Println("no match") }
この場合でも実行結果はzero
のみになる。このことからも最初のcaseに該当したら終了することがわかる。ここにfall throughを足してみる。
switch { case i == 0: fmt.Println("zero") fallthrough case i == 0: fmt.Println("zero2") case i == 1: fmt.Println("one") case i == 2: fmt.Println("two") default: fmt.Println("no match") }
すると出力結果が以下のようになる。
$ go run statement.go zero zero2
これはfall throughが設定されたcaseでは、switch文をbreakせず次のcaseに入るからである。念のため条件が違うi == 1
の方も見てみる。
switch { case i == 0: fmt.Println("zero") fallthrough case i == 0: fmt.Println("zero2") fallthrough case i == 1: fmt.Println("one") case i == 2: fmt.Println("two") default: fmt.Println("no match") }
この場合の実行結果はこうなる。つまりcaseに書かれた条件を無視して次のcase処理を行うのがfall throughである。便利そうだが、動きに癖があるので使う場合は注意をしよう。
$ go run statement.go zero zero2 one
forの使い方
while
がないのでfor
でがんばる模様。
通常
func main() { sum := 0 for i := 1; i <= 100; i++ { sum += i } fmt.Println("sum of 1..100 is", sum) }
条件付きループ
func main() { var num int64 rand.Seed(time.Now().Unix()) for num != 5 { num = rand.Int63n(15) fmt.Println(num) } }
無限ループ
func main() { var num int32 sec := time.Now().Unix() rand.Seed(sec) for { fmt.Print("Writting inside the loop...") if num = rand.Int31n(10); num == 5 { fmt.Println("finish!") break } fmt.Println(num) } }
defer / panic / reccover
defer
- deferをつけた関数の実行を、他の関数の実行後に回す役割を持つ
- deferをつける関数はいくつでもよく、つけた関数は後入れ先出し(LIFO)で実行される
- 利用ケースとしてはファイルのクローズ処理など
package main import "fmt" func main() { for i := 1; i <= 4; i++ { defer fmt.Println("deferred", -i) fmt.Println("regular", i) } }
結果↓
regular 1 regular 2 regular 3 regular 4 deferred -4 deferred -3 deferred -2 deferred -1
panic
- 通常の処理(自分で書いているもの)は停止する
- ただしdeferで後回しにした処理は実行される
package main import "fmt" func main() { g(0) fmt.Println("Program finished successfully!") } func g(i int) { if i > 3 { fmt.Println("Panicking!") panic("Panic in g() (major)") } defer fmt.Println("Defer in g()", i) fmt.Println("Printing in g()", i) g(i + 1) }
Printing in g() 0 Printing in g() 1 Printing in g() 2 Printing in g() 3 Panicking! Defer in g() 3 Defer in g() 2 Defer in g() 1 Defer in g() 0 panic: Panic in g() (major) goroutine 1 [running]: main.g(0x4) /Users/johndoe/go/src/helloworld/main.go:13 +0x22e main.g(0x3) /Users/johndoe/go/src/helloworld/main.go:17 +0x17a main.g(0x2) /Users/johndoe/go/src/helloworld/main.go:17 +0x17a main.g(0x1) /Users/johndoe/go/src/helloworld/main.go:17 +0x17a main.g(0x0) /Users/johndoe/go/src/helloworld/main.go:17 +0x17a main.main() /Users/johndoe/go/src/helloworld/main.go:6 +0x2a exit status 2
recover
- パニックの後に制御を取り戻すことができる
- recover関数はdeferの中でのみ利用できる
package main import "fmt" func main() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in main", r) } }() g(0) fmt.Println("Program finished successfully!") } func g(i int) { if i > 3 { fmt.Println("Panicking!") panic("Panic in g() (major)") } defer fmt.Println("Defer in g()", i) fmt.Println("Printing in g()", i) g(i + 1) }
この場合は以下の出力となる。
Printing in g() 0 Printing in g() 1 Printing in g() 2 Printing in g() 3 Panicking! Defer in g() 3 Defer in g() 2 Defer in g() 1 Defer in g() 0 Recovered in main Panic in g() (major)