Go言語入門 学習メモ 2 演算子、制御構文

# はじめに
船井総研デジタルのoswです。業務でGo言語を使うことになったのでこれから学習していきます。その備忘録です。参考になる方がいらっしゃれば幸いです。

# 対象読者
- これからGo言語を学習する方
- 既に他の言語で基本構文を学習されている方

# 学習環境
学習環境は次のようになっています。この環境の構築メモは下記記事にまとめてあります。ご興味がある方はご参照ください。
- Windows 11 Home / 22H2
- VSCode / 1.72.2
- go version go1.19.2 windows/amd64
- git version 2.38.0.windows.1

https://qiita.com/fsd-osw/items/0e569b0b828455cdef9e

# 前回までの学習
前回は変数、定数などを学習しました。

https://qiita.com/fsd-osw/items/5d542c367d0bebb403b1

# 演算子
## 四則演算
他の言語と同じようです。

## 比較演算子
他の言語と同じようです。

## 代入演算子
|演算子|内容|
|:-:|:-|
|=|変数へ代入|
|:=|変数を初期化し、代入|
|+=| x = x + y と同等|
|-=| x = x- y と同等|
|x++|後置インクリメントのみ。 x += 1 と同等|
|x--|後置デクリメントのみ。 x -= 1 と同等|

Goのインクリメントは式ではなく文になるため、関数の引数に渡しつつインクリメントするようなことはできない。
```go:サンプルコード
package main

import "fmt"

func main() {
	i := 10

	// これはできない
	// Xxx(i++);

	fmt.Println(i)
}
```

## アドレス演算子
GoはCと同じくポインタが使えるようでアドレスを取得する演算子が用意されています。ただ、Cのようにアドレスの減算など計算はできないようです。
|演算子|内容|
|:-:|:-|
|&|アドレスを取得|
|*|アドレスが指す先を参照する|

```go:サンプルコード
package main

import "fmt"

func main() {
	var num   int = 100
	var pNum *int = &num

	// numのアドレスを表示
	fmt.Printf(" &num: %p\n", &num)
	fmt.Printf(" pNum: %p\n", pNum)

	// numの値を表示
	fmt.Printf("  num: %d\n", num)
	fmt.Printf("*pNum: %d\n", *pNum)
}
```

きちんとアドレスを取得し、それを参照出来ていることがわかります。
```go:実行結果
 &num: 0xc0000180b8
 pNum: 0xc0000180b8
  num: 100
*pNum: 100
```

Printfの書式指定は下記記事が参考になります。書式指定以外も丁寧にまとめて下さっています。

https://zenn.dev/mkosakana/articles/97466310534b4c

## チャネル演算子
|演算子|内容|
|:-:|:-|
|<-|チャネルへ送受信|

# 制御構文
## if文
他の言語と異なり、条件式を()を括らないのがGoらしい書き方のようです。初期化式は省略可です。特徴的なのは{}を省略できない、ifブロックの「{」をifと同じ行に書かなければならない、ですね。
```go:構文
if 初期化式1; 条件1 {
    // 処理1
} else if 初期化式2; 条件2 {
    // 処理2
} else {
    // 処理3
}
```

ifの初期化式は初期化したブロックを含み、それ以降のブロックで有効な様子。例えば次のサンプルではelseに入りますが、ifの中でy := 1しているので、else if / elseの中でも y == 1 となっています。出力結果も y == 1 であることが確認できます。
```go:サンプルコード
package main

import "fmt"

func main() {
	x := 2

	if y := 1; x == 0 {
		fmt.Println("flow1")
		fmt.Println("y:", y) // y: 1
	} else if x == 1 {
		fmt.Println("flow2")
		fmt.Println("y:", y) // y: 1
	} else {
		fmt.Println("flow3")
		fmt.Println("y:", y) // y: 1
	}

	// ()で囲むことも可能
	if (x == 0) {
		fmt.Println("flow1")
	}

	// たとえ1文だとしても{}は省略できない
	/*
	if x == 0
		fmt.Println("flow5")
	*/

	// { は ifと同じ行になければならない
	/*
	if (x == 0)
	{
		fmt.Println("flow6")
	}
	*/
}
```

```go:実行結果
flow3
y: 1
```

## switch文
if文同様に初期化式を書けるようになっており、caseの中で参照できるようになっているようです。また、コンパイラが自動的にbreakを挿入するため、caseの中でbreakする必要がありません。逆に自動でbreakしてしまうため、caseを跨ぎたい場合は、「fallthrough」を使うようです。
（明示的にbreakしても問題ありません）
```go:構文
switch 初期化式; 式 {
case 式1:
    // 処理1
case 式2:
    // 処理2
default:
    // 処理3
}
```

複数のcaseに対応させたい処理は","区切りで記述することで対応できます。次の例では num == 4, または num == 5 のときにcase 4, 5:に入ります。
```go:サンプルコード
package main

import "fmt"

func main() {

	condition := 5
	caseNum := 1

	switch tmp := 100; condition {
	case 1:
		fmt.Println("flow1")
		fmt.Println("tmp:", tmp)

	// 変数を用いた演算も可能
	case caseNum + 1:
		fmt.Println("flow2")
		fmt.Println("tmp:", tmp)

	case 3:
		// caseを並べても自動的にbreakするためcase4には入らない

	case 4, 5:
		fmt.Println("flow4")
		fmt.Println("tmp:", tmp)

		// caseを跨ぎたいならfallthroughを使う
		fallthrough

	case 6:
		fmt.Println("flow6")
		fmt.Println("tmp:", tmp)

	default:
		fmt.Println("default")
		fmt.Println("tmp:", tmp)
	}
}
```

```go:実行結果
flow4
tmp: 100
flow6
tmp: 100
```

<br>

また、switchはif文の代わりとして使うこともできるようです。switch直後の式を省略するとcaseに条件式を書くことができます。（defaultはelseとして機能）
条件の多いif文を置き換えれば、スッキリして視認性が良くなりそうですね。
```go:構文
switch {
case 条件式1:
    // 処理1
case 条件式2:
    // 処理2
default:
    // 処理3
}
```

```go:サンプルコード
package main

import "fmt"

func main() {
	num := -1

	switch {
	case num < -2 || 100 < num:
		fmt.Println("エラー: 範囲外です")

	case 80 <= num:
		fmt.Println("合格")

	case 40 <= num:
		fmt.Println("不合格")

	default:
		fmt.Println("エラー: 不明なエラーです")
	}
}
```

```go:実行結果
エラー: 不明なエラーです
```

# おわりに
今回はここまでです。