Goの学習を行った記録としてこの記事に残す。
この記事では Goの制御構造についてまとめていく。
if
他の言語のifとほとんど変わらない。
Go言語のifでは条件を(…)で囲まない。
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano()) // シードの指定
n := rand.Intn(10) // 0以上10未満の整数を戻す
if n == 0 {
fmt.Println("少し小さすぎます:", n)
} else if n > 5 {
fmt.Println("大きすぎます:", n)
} else {
fmt.Println("いい感じの数字です:", n)
}
} //listend
必要なときだけ変数を宣言して利用できる。
一群のif/elseが終わるとnは未定義なる。
func main() {
rand.Seed(time.Now().Unix())
if n := rand.Intn(10); n == 0 { // nはelseのブロックまで有効になる
fmt.Println("少し小さすぎます:", n)
} else if n > 5 {
fmt.Println("大きすぎます:", n)
} else {
fmt.Println("いい感じの数字です:", n)
}
}
for
Go言語でもループにはforを使う。
for以外のループはないが、4種類のforがある。
- 「初期設定」「条件」「再設定」の三つを指定する、標準的なもの
- 条件部分のみを指定するもの
- 無限ループ
- for-rangeを使うもの
-
標準形式のfor
お馴染みのもの。
func main() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
-
条件部分のみのfor
標準形式の「初期化」「再設定」を省いたもの。c言語などのwhileの代わりになる。
i := 1
func main() {
for i < 10 {
fmt.Println(i)
i = i + 1
}
}
-
無限ループ
条件部分もないfor。
無限ループを抜け出すにはbreakを使う。 -
for-range
Goの組み込み型の各要素に対して繰り返し処理を行う。
for-rangeを使えるのは組み込みの合成型とそれらをベースにしたユーザー定義型.
vals := []int{1,2,3,4,5}
for i, v := range vals {
fmt.Println(i, v)
}
//0 1
//1 2
//2 3
//3 4
//4 5
for-rangeにはループ変数が二つある。
一つ目は変数が繰り返されるデータ構造のキー、二つ目はそのキーの値。
配列やスライス、文字列の各要素を処理する場合はi(index)、マップの場合はk(key)が一つ目の変数によく使われる。
二つ目の変数はv(value)がよく使われる。
キーへのアクセスが不要な場合は「 _ 」を使う。変数として宣言した場合必ず使わなければならなくなるので不要な場合は前述の記述をする必要がある。
vals := []int{1,2,3,4,5}
for _, v := range vals {
fmt.Println(v)
}
-
mapのイテレーション
mapの繰り返し処理の場合、キーや値の順番はいつも同じにならない、
これはセキュリティを考慮した仕様。
理由- 順番が固定されていると仮定したコードを書く人がいるかもしれないが、これは保たれる保証がない。
- 「HashDos」と呼ばれる攻撃の対策
※fmt.Printlnなどはマップの要素を同じ順で返す。
-
文字列に対してのイテレーション
文字列に対してもループを使うことができる。
キーは文字列の先頭からのバイト数になり、値の型はruneになる。
package main
import "fmt"
func main() {
samples := []string{"hello", "apple_π!", "これは漢字文字列"} //liststart
for _, sample := range samples {
for i, r := range sample {
fmt.Println(i, r, string(r))
}
fmt.Println()
} //listend
}
0 104 h
1 101 e
2 108 l
3 108 l
4 111 o
0 97 a
1 112 p
2 112 p
3 108 l
4 101 e
5 95 _
6 960 π
8 33 !
0 12371 こ
3 12428 れ
6 12399 は
9 28450 漢
12 23383 字
15 25991 文
18 23383 字
21 21015 列
漢字やπなどのマルチバイトのruneに出会うとUTF-8の表現を32ビットの数値に変換した値を返す。
オフセットはruneのバイト数だけ増える。
-
for-rangeの値はコピー
for-rangeが合成型に関していてレーションする際にはいつも、合成型の各要素の値が変数にコピーされる。コピーされた変数の値を変更しても、元々の合成型の値は変わらない。
func main() {
evenVals := []int{2, 4, 6, 8, 10, 12}
for _, v := range evenVals {
v *= 2
}
fmt.Println(evenVals) // [2 4 6 8 10 12]
}
switch
Goのswitchはif文同様比較対象の変数は(…)で囲まない。
switc文でもfor文同様、変数を宣言して全ての分岐で使えるスコープを持たせることができます。
他の言語では各caseの最後にbreakを書くことが多いが、Goでは書かない。
Goのswitch文のcaseではフォロースルーしない。
複数の値に対して同じ処理をしたい場合、「,」で区切って複数列挙すれば良い。
package main
import (
"fmt"
"unicode/utf8" // utf8.RuneCountInString(word)で、wordの文字数を数えられる
)
func main() {
words := []string{"山", "sun", "微笑み", "人類学者", "モグラの穴", "mountain",
"タコの足とイカの足", "antholopologist","タコの足は8本でイカの足は10本"}
for _, word := range words {
switch size := utf8.RuneCountInString(word); size {
case 1, 2, 3, 4:
fmt.Printf("「%s」の文字数は%dで、短い単語だ。\n", word, size)
case 5:
fmt.Printf("「%s」の文字数は%dで、これはちょうどよい長さだ。\n", word, size)
case 6, 7, 8, 9:
default:
fmt.Printf("「%s」の文字数は%dで、とても長い。", word, size)
if n := len(word); size < n {
fmt.Printf("%dバイトもある!\n", n)
} else {
fmt.Println()
}
}
}
}
- ブランクswitch
比較対象の変数などを指定しないswitchもある、これをブランクswitchという。
ブランクswitchは各caseに対して論理的な比較が可能。
== で比較できるものならばcaseの条件に使える。
つまり、組み込み型のうち、スライス、マップ、チャネル、関数、およびこれらを含む構造体を除く型を使える。
package main
import "fmt"
func main() {
words := []string{"hi", "salutations", "hello"} //liststart
for _, word := range words {
switch wordLen := len(word); { // 比較対象の変数の指定なし
case wordLen < 5:
fmt.Println(word, "は短い単語です")
case wordLen > 10:
fmt.Println(word, "は長すぎる単語です")
default:
fmt.Println(word, "はちょうどよい長さの単語です")
}
} //listend
}