Closed15

A Tour of Go やってみた Part2

kun432kun432

Basics: Flow control statements: for, if, else, switch and defer

1. For

forはごくごく一般的な 「初期化; 条件式; インクリメント」で書くだけ。カッコで括る必要はないけど、中括弧でブロックを指定する必要がある。初期化時に短い変数宣言がよく使われる。またこの変数はforのスコープ内でのみ有効になる。

1.go
package main

import "fmt"

func main() {
	sum := 0
	fmt.Println("Start:", sum)
	for i := 0; i< 10; i++ {
		sum += 1
		fmt.Println("Current:", sum)
	}
	fmt.Println("End:", sum)
}
出力
Start: 0
Current: 1
Current: 2
Current: 3
Current: 4
Current: 5
Current: 6
Current: 7
Current: 8
Current: 9
Current: 10
End: 10
kun432kun432

2. For continued

初期化と後処理は任意なので書かないことも可能。

2.go
package main

import "fmt"

func main() {
	sum := 1
	fmt.Println("Start:", sum)
	for ; sum < 10; {
		sum += sum
		fmt.Println("Current:", sum)
	}
	fmt.Println("End:", sum)
}
出力
Start: 1
Current: 2
Current: 4
Current: 8
Current: 16
End: 16
kun432kun432

3. For is Go's "while"

セミコロンを省略すると、一般的なwhileとおなじになる。

3.go
package main

import "fmt"

func main() {
	sum := 1
	fmt.Println("Start:", sum)
	for sum < 10 {
		sum += sum
		fmt.Println("Current:", sum)
	}
	fmt.Println("End:", sum)
}
出力
Start: 1
Current: 2
Current: 4
Current: 8
Current: 16
End: 16
kun432kun432

4. Forever

何も条件を指定せずforだけだと無限ループになる。

4.go
package main

import "fmt"

func main() {
	sum := 1
	fmt.Println("Start:", sum)
	for {
		sum += 1
		fmt.Println("Current:", sum)
	}
}

停止条件がないのでctrl+cで止める。

出力
Start: 1
Current: 2
Current: 3
Current: 4
Current: 5
Current: 6
Current: 7
Current: 8
Current: 9
Current: 10
(snip)
kun432kun432

5. If

forと同じく条件をカッコで括る必要はない

5.go
package main

import (
	"fmt"
	"math"
)

func sqrt(x float64) string {
	// 負の数の場合、虚数部分を追加
	if x < 0 {
		return sqrt(-x) + "i"
	}
	// 平方根を計算して文字列に変換
	return fmt.Sprint(math.Sqrt(x))
}

func main() {
	fmt.Println(sqrt(2), sqrt(-4))
}
出力
1.4142135623730951 2i
kun432kun432

6. If with a short statement

ifもforと同じ初期化ステートメントが使えて、ここで初期化された変数はifのスコープ内だけ有効になる。

6.go
package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	// 変数vはifスコープ内でのみ有効
	// xのn乗を計算して、それがlimより小さいか
	if v := math.Pow(x, n); v < lim {
		return v
	}
	// ここではvは使えない
	return v
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}
出力
# command-line-arguments
./6.go:12:9: undefined: v

これなら動くのでスコープが有効なことがわかる。

6-2.go
package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	v := math.Pow(x, n)
	if v < lim {
		return v
	}
	return -v
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}
出力
9 -27
kun432kun432

7. If and else

if {〜} else {〜} という形。ifで初期化された変数はelse内でも使える。

7.go
package main

import (
	"fmt"
	"math"
)

func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
	return lim
}

func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
}
出力
27 >= 20
9 20
kun432kun432

9. Switch

選択されたcaseだけが実行されてそれ以外は実行されない、よってbreakを書く必要がない。また、ラベルに変数や計算式を直接書いてもよいし、整数以外の型(文字列、浮動小数点、さらにはユーザー定義型など)も使える。

9.go
package main

import (
	"fmt"
	"runtime"
)

func main() {
	fmt.Print("Go runs on ")
	os := runtime.GOOS
	switch os {
	case "darwin":
		fmt.Print("macOS")
	case "linux":
		fmt.Print("Linux")
	default:
		fmt.Print("%s", os)
	}
	arch := runtime.GOARCH
	switch arch {
	case "amd64":
		fmt.Println("/AMD64.")
	case "386":
		fmt.Println("/386.")
	case "arm64":
		fmt.Println("/ARM64.")
	default:
		fmt.Printf("/%s.\n", arch)
	}
}

自分の場合はM2 Macなので。

Go runs on macOS/ARM64.
kun432kun432

10. Switch evaluation order

switch caseは上から順に評価し、caseが一致すればそこで停止する。

10.go
package main

import "fmt"

func f() int {
	fmt.Println("f() called. return 1")
	return 1
}

func main() {
	i := 0
	switch i {
	case 0:
		fmt.Println("case 0")
	case f():
		fmt.Println("case f()")
	}
}

評価すらされていないということか。

出力
case 0
10-2.go
package main

import (
	"fmt"
	"time"
)

func main() {
	fmt.Println("土曜日はいつですか?")
	today := time.Now().Weekday()
    fmt.Println(today, time.Saturday)
	switch time.Saturday {
	case today + 0:
		fmt.Println("今日です。")
	case today + 1:
		fmt.Println("明日です。")
	case today + 2:
		fmt.Println("明後日です。")
	default:
		fmt.Println("まだ先です。。。")
	}
}
出力
土曜日はいつですか?
Tuesday Saturday
まだ先です。。。
kun432kun432

11. Switch with no condition

条件無しで書くこともできる。if-then-elseをシンプルに書ける。

11.go
package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	switch {
	case t.Hour() < 12:
		fmt.Println("おはよう!")
	case t.Hour() < 17:
		fmt.Println("こんにちは!")
	default:
		fmt.Println("こんばんは!")
	}
}
出力
こんにちは!
kun432kun432

12. Defer

deferに関数を渡すと、defer呼び出し元の関数が終了(return)するまで遅延させることができる。評価自体はdeferのタイミングで行われるが、実行は呼び出し元関数が終了してから。

12.go
package main

import "fmt"

func main() {
	defer fmt.Println("遅れて実行されます。")
	fmt.Println("こんにちは。")
}
出力
こんにちは。
遅れて実行されます。
kun432kun432

13. Stacking defers

deferに複数の関数を渡すと、呼び出しがスタックされて、呼び出し元がreturnしたタイミングでLIFOで実行される。

13.go
package main

import "fmt"

func main() {
	fmt.Println("数えます")

	for i :=0; i < 10; i++ {
		defer fmt.Println(i)
	}
	fmt.Println("終了しました")

}
出力
開始します
終了しました
9
8
7
6
5
4
3
2
1
0
このスクラップは3ヶ月前にクローズされました