APG4g（Go言語入門 APG4b）【1.03 ~ 1.04】

# 本記事について

本記事は、[APG4g（Go言語入門 APG4b）【1.00 ~ 1.02】](https://qiita.com/Nub/items/f8c424e00a8b2ff593f8)の続きです。
もし、前の記事を読んでいない、もう一度見たい等ありましたら見ていただけると幸いです。

今回扱う内容は以下の通りです。

- [1.03. 四則演算と優先順位](#103-四則演算と優先順位)
- [1.04. 変数と型](#104-変数と型)

本記事での目標は、以下の3点です。

- Go言語で、四則演算とあまりの計算が出来るようになる
- Go言語で、データを保存する変数を作れるようになる
- Go言語にあるデータの型を知り、使えるようになる

では、始めていきましょう！

## [1.03. 四則演算と優先順位](https://atcoder.jp/contests/apg4b/tasks/APG4b_d)

### キーポイント

| 演算子 | 計算内容     |
| ------ | ------------ |
| `+`    | 足し算       |
| `-`    | 引き算       |
| `*`    | かけ算       |
| `/`    | 割り算       |
| `%`    | 割ったあまり |

### 四則演算

Go言語のプログラムで簡単な計算をする方法を見ていきましょう。

次のプログラムは上から順に「足し算」「引き算」「掛け算」「割り算」を行っています。

```go: d_1.03a.go
package main

import "fmt"

func main() {
    fmt.Println(1 + 1) // 2　（足し算）

    fmt.Println(3 - 4) // -1　（引き算）

    fmt.Println(2 * 3) // 6　（かけ算）

    fmt.Println(7 / 3)     // 2　（割り算）
    fmt.Println(-7 / 3)    // -2　（負の値の割り算）
    fmt.Println(7.0 / 3) // 小数の割り算
}
```

```text:実行結果
2
-1
6
2
-2
2.3333333333333335
```

これらの記号（`+`,`-`,`*`,`/`）のことを**算術演算子**といいます。`+`,`-`,`*`,`/`は順に、「足し算」「引き算」「かけ算」「割り算」に使う演算子（記号）です。

引き算の例のように、（`+`,`-`,`*`,`/`）を使って負の値も計算することが出来ます。

Go言語では、整数同士で割り算（`/`）をした場合、小数点以下を切り捨てた値になります。

負の値の割り算も小数点が切り捨てられます。
切り捨ててほしくない場合、`7.0`というように`.0`をつけると、小数点以下も計算されます。

### 剰余演算

`%`を使うことで、「割った時のあまり」を計算することが出来ます。Go言語は負の値のあまりも計算することが出来ます。

```go: d_1.03b.go
package main

import "fmt"

func main() {
    fmt.Println(-4 % 3) // -1
    fmt.Println(-3 % 3) // 0
    fmt.Println(-2 % 3) // -2
    fmt.Println(-1 % 3) // -1

    fmt.Println(0 % 3) // 0

    fmt.Println(1 % 3) // 1
    fmt.Println(2 % 3) // 2
    fmt.Println(3 % 3) // 0
    fmt.Println(4 % 3) // 1
}
```

```text: 実行結果
-1
0
-2
-1
0
1
2
0
1
```

### 演算子の優先順位

C++及びPythonの優先順位と変わりません。

- 最初に、かけ算、割り算（`*`,`/`）が計算される
- 次に、足し算、引き算（`+`,`-`）が計算される
- 括弧`()`で括られた計算があるなら、括弧の中の計算が優先される

### ゼロ除算（細かい話）

**Go言語ではゼロで割っても実行時エラーになりません。**
※ただし、数値を直接指定してゼロ除算をしている場合はコンパイルエラーを起こします。

```go: d_1.03c.go
package main

import "fmt"

func main() {
    zero := 0.0
    pinf := 1.0 / zero
    ninf := -1.0 / zero
    nan := zero / zero

    fmt.Println(zero, pinf, ninf, nan)
    // fmt.Println(1.0 / 0.0) // ./d_1.03c.go:12:20: invalid operation: division by zero
}
```

```text:実行結果
0 +Inf -Inf NaN
```

このように、0や正/負の無限大、NaN（Not a Number）として計算されます。

参考資料

- <https://en.wikipedia.org/wiki/NaN>
- <https://go.dev/ref/spec#Representability>

### 練習問題

[EX3 - 計算問題](https://atcoder.jp/contests/apg4b/tasks/APG4b_ct)を解いてみましょう。

<details><summary>解答（解けなかったら開いてください）</summary>

```go: 解答
package main

import "fmt"

func main() {
    fmt.Println(100 * (100 + 1) / 2)
}
```

#### 補足

なぜ、

```math
\frac{1}{2} \times 100 \times (100 + 1)
```

をそのまま計算すると不正解となってしまうのか？を説明します。

結論から言うと、`1 / 2`は整数の割り算の切り捨て処理によって、`0`となってしまうからです。

実は、`1.0 / 2.0`とすることで回避できます。（`0.5`になる）

ですが、**割り算は最後に計算する**というプログラムを書いたほうが、数値計算として誤差が出にくくなるため、割り算は最後に書いたほうが良いと思います。

```go: d_1.03d.go
package main

import "fmt"

func main() {
    fmt.Println(1 / 2)
    fmt.Println(1.0 / 2.0 * 100 * (100 + 1))
}
```

```text: 実行結果
0
5050
```

</details>

## [1.04. 変数と型](https://atcoder.jp/contests/apg4b/tasks/APG4b_e)

### キーポイント

- 変数は「メモ」
- `=`は代入
- 「データの種類」を「型」と言う

| 型      | 書き込むデータの種類 |
| ------- | -------------------- |
| int     | 整数                 |
| float64 | 小数                 |
| string  | 文字列               |

### 変数

変数という機能について見ていきましょう。変数は「メモ」や「ふせん」というイメージがいいかもしれません。

メモはこのようなイメージです。詳しくは後述します。

- 変数名 : メモのタイトル
- 型 : メモに書いてあるデータの種類と紙の大きさ
- 値 : メモの内容

例を見てみましょう。

```go: e_1.04a.go
package main

import "fmt"

func main() {
    var variable_1 int = 10
    var variable_1a int
    variable_2 := 20

    fmt.Println(variable_1, variable_1+10)
    fmt.Println(variable_1a, variable_1a+10)
    fmt.Println(variable_2, variable_2+10)
}
```

```text:実行結果
10 20
0 10
20 30
```

#### 宣言と代入

変数を使うには最初に「宣言」を行う必要があります。
変数を宣言する方法には2種類ありますが、どちらを使っても構いません。

変数を宣言する時は、変数の名前を指定します。

```go :変数の宣言をする例
var variable_1 int = 10 // 明示的型付け
var variable_1a int // 明示的型付け その2
variable_2 := 20 // 暗黙的型付け
```

1行目の明示的型付けで宣言を行う場合、最初に`var`を付けます。
その後に変数名（`variable_1`）を書き、最後に型（`int`）を付けます。

2行目の暗黙的型付けの場合は変数名（`variable_2`）を書き、`:=`を書いて、その後にメモの内容となる値（データ）を書きます。

※暗黙的型付けの場合、明示的型付けで行うような型の指定はコンパイル時に自動的に解釈されます。

宣言した変数にデータをメモする時は、`=`を使って代入をします。
また、宣言時に値を同時に代入することを初期化と言います。

明示的型付けの場合は初期化をする必要がありませんが、暗黙的型付けの場合は`:=`を使った初期化が必要です。

#### 変数のコピー

`変数1 = 変数2`というような形で書いた場合、変数の値そのものがコピーされます。

その後に変数の値を変えても、もう片方の変数に影響はありません。

```go: e_1.04b.go
package main

import "fmt"

func main() {
    var a int = 10
    var b = a
    a = 40
    fmt.Println(a, b)
}
```

```text:実行結果
40 10
```

#### 変数を同時に宣言

変数の宣言時に`,`を入れることで、複数の変数を同時に宣言することも出来ます。

```go: e_1.04c.go
package main

import "fmt"

func main() {
    var a, b int = 10, 5 // 明示的型付け
    c, d := 20, 25       // 暗黙的型付け

    fmt.Println(a, b)
    fmt.Println(c, d)
}
```

```text:実行結果
10 5
20 25
```

さらに、明示的宣言の場合は`()`で囲んで改行区切りで書くことで、1つの`var`で複数行に渡って変数を宣言することが出来ます。

```go: e_1.04d.go
package main

import "fmt"

func main() {
    var (
        a int
        b int
        c int = 5
        d int = 10
    )
    fmt.Println(a, b, c, d)
}
```

```text: 実行結果
0 0 5 10
```

### 変数のルール

変数は基本的に自由につけることが出来ますが、一部の名前を変数名にするとコンパイルエラーを起こします。

#### 利用できない変数名

以下の条件に該当する変数名は変数名に出来ません。

- 数字で始まる名前
- `_`以外の記号が使われている名前
- キーワード（Go言語が使っている一部の単語）

例えば、以下のコードはエラーになります。

```go: e_1.04e.go
package main

import "fmt"

func main() {
    var 100hello int
    var na+me
    var go int
    fmt.Println(100hello, na+me, go)
}
```

```text: エラーログ
./e_1.04e.go:6:6: syntax error: unexpected literal 100, expected name
./e_1.04e.go:7:8: syntax error: unexpected +, expected type
./e_1.04e.go:8:6: syntax error: unexpected go, expected name
./e_1.04e.go:9:17: syntax error: unexpected hello in argument list; possibly missing comma or )
```

以下のような変数名は大丈夫です。

```go: 問題ない変数名
var hello10 int
var hello_world int
```

#### 同じ変数名の変数は宣言できない

同じ名前の変数を複数回宣言することは出来ません。

```go: e_1.04f.go
package main

import "fmt"

func main() {
    var a = 0
    var a = 5
    fmt.Println(a)
}
```

```text: エラーログ
./e_1.04f.go:7:6: a redeclared in this block
        ./e_1.04f.go:6:6: other declaration of a
```

### 型

`int`以外にもGoには様々な型があります。
ここでは重要な3つの型を紹介します。

| 型      | 書き込むデータの種類 |
| ------- | -------------------- |
| int     | 整数                 |
| float64 | 小数                 |
| string  | 文字列               |

```go : e_1.04g.go
package main

import "fmt"

func main() {
    i := 10
    f := 0.5
    s := "Hello"

    fmt.Println(i, f, s)
}
```

```text: 実行結果
10 0.5 Hello
```

### 異なる型同士の計算

Go言語では異なる型同士の計算が出来ません。
異なる型同士の計算をしようとすると、コンパイルエラーとなります。

```go: e_1.04h.go
package main

import "fmt"

func main() {
    a := 1
    b := 1.5
    fmt.Println(a * b)
}
```

```text:エラーログ
./e_1.04h.go:9:14: invalid operation: a * b (mismatched types int and float64)
```

### 異なる型同士の代入

Go言語では異なる型同士の代入も出来ません。
[異なる型同士の計算](#異なる型同士の計算)と同様にコンパイルエラーとなります。

```go: e_1.04h.go
package main

import "fmt"

func main() {
    i := 1
    var d float64 = i
    fmt.Println(i, d)
}
```

```text:エラーログ
./e_1.04i.go:7:18: cannot use i (variable of type int) as float64 value in variable declaration
```

#### 細かい話

<details><summary>変数の細かい話です</summary>

Go言語では`int`はキーワードではないので、
`var int int`という変数宣言が可能です。
（コードを読む際に混乱することになるので、書くべきではありません）

```go:e_1.04e_ex.go
package main

import "fmt"

func main() {
    var int int // intの初期値は0
    fmt.Println(int)
}
```

```text:実行結果
0
```

Go言語のキーワードについては、[Keywords - The Go Programming Language Specification](https://go.dev/ref/spec#Keywords)を御覧ください。

</details>

### 練習問題

[EX4 - ◯年は何秒？](https://atcoder.jp/contests/apg4b/tasks/APG4b_cs)を解いてみましょう。

```go: コピーするプログラム
package main

import "fmt"

func main() {
    seconds := 365 * 24 * 60 * 60

    // 文字列の部分を消して追記する
    fmt.Println("1年は何秒か")
    fmt.Println("2年は何秒か")
    fmt.Println("5年は何秒か")
    fmt.Println("10年は何秒か")
}
```

<details><summary>解答（解けなかったら開いてください）</summary>

```go: 解答
package main

import "fmt"

func main() {
    seconds := 365 * 24 * 60 * 60

    // 文字列の部分を消して追記する
    fmt.Println(seconds)
    fmt.Println(seconds * 2)
    fmt.Println(seconds * 5)
    fmt.Println(seconds * 10)
}
```

</details>

## 第一章 （1.03 - 1.04）　おわりに

APG4gの1.03 - 1.04までの内容は以上です。お疲れさまでした。

以降の内容は別途投稿する予定です。
より良い書き方がありましたら適宜ご指摘お願いします。

## 次に進むならこちら

[APG4g（Go言語入門 APG4b）【1.05】](https://qiita.com/Nub/items/9f64cd888cbf4e311d4e)に進む

## 参考資料

- AtCoder / APG4b系
    - [APG4b](https://atcoder.jp/contests/APG4b)
    - [Python版 APG4b](https://qiita.com/saba/items/b9418d7b54cce4b106e4)
    - [AtCoder コードテスト](https://atcoder.jp/contests/apg4b/custom_test)
- Go言語ツール系
    - [Go for Visual Studio Code - VS Code Go言語拡張機能リンク](https://marketplace.visualstudio.com/items?itemName=golang.Go)
    - [Go in Visual Studio Code - VS Code Go言語拡張機能 ドキュメント](https://code.visualstudio.com/docs/languages/go#_formatting)
- Go言語の機能系
    - [Keywords - The Go Programming Language Specification](https://go.dev/ref/spec#Keywords)
- その他
    - [NaN - Wikipedia](https://ja.wikipedia.org/wiki/NaN)