Go言語の基本文法（定義編）

[スターフェスティバル Advent Calendar 2020](https://qiita.com/advent-calendar/2020/stafes) の16日目です。

弊社のプロダクト「[ごちクル](https://gochikuru.com/)」の一部処理がGoに改修されたこともあり勉強し始めたので基本的な文法（主に変数などの定義）をまとめてみました。

※ Udemyの[こちら](https://www.udemy.com/course/go-fintech/)の教材を参照元にしています。

# packageとimport

Goではプログラムのエントリーポイントとなるのが`mainパッケージ`の`main関数`。
パッケージのグループは名前空間とも呼ばれる。


```golang

// パッケージの宣言
package main

// ライブラリ
import "fmt"

// main関数の定義と実行
func main() {
  fmt.Println("こんにちは!")
}
// こんにちは!
```

ソースファイルは`必ず１つのパッケージに所属させる必要がある`ため、
今回の場合はmain宣言とmain関数が存在しないとエラーになる。
また1つのファイルに複数のパッケージを設定することはできない。

パッケージはディレクトリ単位で管理される。
1つのディレクトリに複数のパッケージは置けない。

fmt（フォーマット）は、[Goが標準で提供するライブラリ（パッケージ）](https://golang.org/pkg/)の１つ。

# 変数宣言

### `var`を使った変数宣言の場合

```go
package main

import "fmt"

func main() {
  // 色々な型で変数宣言
  var i = 1
  var f64 float64 = 1.2
  var s string = "test"
  var t, f bool = true, false  // 複数の変数宣言も可能

  // 出力
  fmt.Println(i, f64, s, t, f)
}
// 1 1.2 test true false
```

var変数を`()丸括弧`で囲ってまとめて宣言することもできる。

```go
// 省略

func main() {
  var (
    i int = 1
    f64 float64 = 1.2
    s string = "test"
    t, f bool = true, false
  )
  fmt.Println(i, f64, s, t, f)
}
// 1 1.2 test true false
```

### `:=`を使った変数宣言
`:=`を使った Short variable declaration の書き方はこう。

```go

// 省略

func main() {
  xi := 1
  xf64 := 1.2
  xs := "test"
  xt, xf := true, false
  fmt.Println(xi, xf64, xs, xt, xf)
}
// 1 1.2 test true false
```

`:=` 使った変数宣言のvarとの違いは、
`関数の中でしか宣言できない`こと。
上記例の場合、`func main() {}`の外で宣言すると、エラーになる。


この宣言では型が指定されていない`untyped`となり、型は自動判定される。
この`untyped`の変数に型を付けたい場合は、下記のようにする。

```go

// 省略

func main() {
  xf64 := 1.2

  fmt.Printf("%T", xf64)
  // 型を確認できるPrintfで確認
  // float64

  var xf32 float32 = 1.2
  // 型をfloat32と明示的に宣言

  fmt.Printf("%T", xf32)
  // 型を確認できるPrintfで確認
  // float32
}
```

型を明示的に宣言したいときは`var`で、
簡単に宣言したいときは`:=`を使う。


### `const`を使った変数宣言

`const`は定数宣言。
変数名は、頭文字を大文字にすると他のファイルからも参照できる`グローバル変数`になる。

```go
const Pi = 3.14
```

constも型を指定しない`untyped`の変数。

# 型について

### 数字型

数値型は下記のように宣言する。
数値型の一覧[ドキュメント](https://golang.org/ref/spec#Numeric_types)はこちら。

```go
package main

import "fmt"

func main() {
  var (
    u8  uint8     = 225
    i8  int8      = 127
    f32 float32   = 0.2
    c64 complex64 = -5 + 12i
  )
  fmt.Println(u8, i8, f32, c64)
  fmt.Printf("type=%T value=%v", u8, u8)
}
// 225 127 0.2 (-5+12i)
// type=uint8 value=225
```
（[Printfのドキュメント](https://golang.org/pkg/fmt/#example_Printf)はこちら）

`Printf`で型やvalueを確認することができる。

```go
// 省略
  x := 1 + 1
  fmt.Println(x)
  fmt.Println("1 + 1 =", 1+1)
  fmt.Println("10 - 1 =", 10-1)
  fmt.Println("10 / 2 =", 10/2)
  fmt.Println("10 / 3 =", 10/3)
  fmt.Println("10.0 / 3 =", 10.0/3)
  fmt.Println("10 / 3.0 =", 10/3.0)
  fmt.Println("10 % 2 =", 10%2)
  fmt.Println("10 % 3 =", 10%3)
}
// 2
// 1 + 1 = 2
// 10 - 1 = 9
// 10 / 2 = 5
// 10 / 3 = 3
// 10.0 / 3 = 3.3333333333333335
// 10 / 3.0 = 3.3333333333333335
// 10 % 2 = 0
// 10 % 3 = 1
```

インクリメントを使うこともできる。

```go
// 省略
  x := 0
  fmt.Println(x) // 0

  x++
  fmt.Println(x) // 1

  x--
  fmt.Println(x) // 0
}
```


### 文字列型
文字列型は下記のように定義できる。

```go
package main

import "fmt"

func main() {
  fmt.Println("Hello World")
  fmt.Println("Hello " + "World")
}
// Hello World
// Hello World
```

#### stringのキャスト
文字列の一番初めの文字を表示したい時、
`fmt.Println("Hello World"[0])`このようにインデックスを指定するだけでは、アスキーコードが取得されてしまい、取得できない。

`string`を使ってキャストする必要がある。

```go
package main

import (
  "fmt"
  "strings" //追加
)

func main() {
  fmt.Println("Hello World"[0]) // 72
  fmt.Println(string("Hello World"[0])) // H
}
```

#### エスケープ
バッククオートで囲うと、エディタの改行がそのまま反映できる。
ダブルクオートの中にダブルクオートを入れたい場合は \（バックスラッシュ）でエスケープ

```go
  fmt.Println(`Test
Test`)

  fmt.Println("\"")
}
// Test
// Test

// "
```

### 論理値型

```go
package main

import "fmt"

func main() {
// %T = 型
// %v = 値
// %t = 単語、true または false 指定した型じゃないと正しく表示されない

//   var t, f bool = true, false
  t, f := true, false
  fmt.Printf("%T %v %t\n", t, t, t)
  fmt.Printf("%T %v %t\n", f, f, f)

// 論理演算子 && （and）
  fmt.Println(true && true) // 真かつ真 = 真
  fmt.Println(true && false) // 真かつ偽 = 偽
  fmt.Println(false && false) // 偽かつ偽 = 偽

// 論理演算子 || （or）
  fmt.Println(true || true) // 真もしくは真 = 真
  fmt.Println(true || false) // 真もしくは偽 = 真
  fmt.Println(false || false) // 偽もしくは偽 = 偽

// 論理演算子 ! （not）
  fmt.Println(!true) // false 条件の反対の結果
  fmt.Println(!false) // true 条件の反対の結果
}
```

### 型変換（キャスト）
Goでは`integer -> float`のような数値同士の型変換は簡単にできるが、`文字列 -> 数値`のような型変換は少しコツがいる。

#### integer->floatへの型変換（簡単）
下記のようにスムーズに変換できる。
（[Printfのドキュメント](https://golang.org/pkg/fmt/#example_Printf)はこちら）


```go
package main

import "fmt"

func main() {
  var x int = 1
  xx := float64(x) // int を float64 に型変換
  
  fmt.Printf("%T %v %f\n", xx, xx, xx)
}
// float64 1 1.000000
// int -> float64 へ型変換できている
```


#### `string -> int`に型変換（ちょっとコツいる）
文字列の相互変換（コンバージョン）用のライブラリ`strconv`を使用して型変換する。
（[strconvのドキュメント](https://golang.org/pkg/strconv/)はこちら）

```go
package main

import (
  "fmt"
  "strconv" //追加
}

func main() {
  var s string = "14"
  i, _ := strconv.Atoi(s) // int型に変換
  fmt.Printf("%T %v\n", i, i)
}
// int 14
// string -> int へ型変換できている
```

[Atoi関数](https://golang.org/pkg/strconv/#example_Atoi)はAscii to integerの略。

`i, _ := strconv.Atoi(s)`の`_`について。
Atoiは返り値を２つ（int, error）返す関数。

２つ目の返り値はエラーハンドリング用だが、
`i := strconv.Atoi(s)`このように省略するとエラーになってしまう。

今回のように使わない場合は`_`で省略することができる。


# 配列
goでの配列の基本的な書き方はこんな感じ。
`[]`スクエアブラケットの中身は、配列の個数が入る。
配列の場合は、`[2]int` この部分が型になる。

```go
package main

import "fmt"

func main() {
  // 配列
  var a [2]int
  a[0] = 100
  a[1] = 200
  fmt.Println(a)


  // この書き方も可
  var b [2]int = [2]int{100, 200}
  fmt.Println(b)
}
// [100 200]
// [100 200]
```

配列は`[2]int`このように配列の個数と型が決まっているため、配列に`append`などで追加してリサイズすることはできず、エラーとなる。

```go
// 省略

func main() {
  var b [2]int = [2]int{100, 200}
  b = append(b, 300) //このように配列に追加しようとするとエラー
  fmt.Println(b)
}
// first argument to append must be slice; have [2]int
// b redeclared in this block
```

ではどうやって追加するのか？
配列ではなく`スライス`を使用する。

### スライス
スライスは配列と違い、個数を指定せず下記のように宣言する。

```go
// 配列
var b [2]int = [2]int{100, 200}

// スライス
var b []int = []int{100, 200}
```

スライスは個数を追加することができる。

```go
package main

import "fmt"

func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  // スライスに値を追加
  n = append(n, 100, 200, 300, 400)
  fmt.Println(n)
}
// [1 2 3 4 5 6 100 200 300 400]
```

配列はリサイズできない。
スライスはリサイズできる。

#### スライスにインデックスを指定する
スライスをインデックス指定で取得できる。
●〜●番目という風にレンジで取得する際の数え方が独特。

```go
package main

import "fmt"

func main() {
  n := []int{1, 2, 3, 4, 5, 6}

  // 配列の●番目
  fmt.Println(n[2])

  // 配列の●〜●番目
  fmt.Println(n[2:4])

  // 配列の〜●番目まで
  fmt.Println(n[:2])

  // 配列の●番目以降
  fmt.Println(n[2:])

  // 配列すべて表示
  fmt.Println(n[:])
}
// 3
// [3 4]
// [1 2]
// [3 4 5 6]
// [1 2 3 4 5 6]
```

#### スライスの値を書き換える
指定したインデックスの値を書き換える。

```go
// 省略
func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  n[2] = 100
  fmt.Println(n)
}
// [1 2 100 4 5 6] 
```

#### スライスを入れ子にする

```go
// 省略
func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  var board = [][]int{
    []int{0, 1, 2},
    []int{3, 4, 5},
    []int{6, 7, 8},
  fmt.Println(board)
}
// [[0 1 2] [3 4 5] [6 7 8]]
```

#### スライスに追加する

```go
// 省略
func main() {
  n := []int{1, 2, 3, 4, 5, 6}
  n = append(n, 100, 200, 300, 400)
  fmt.Println(n)
}
// [1 2 3 4 5 6 100 200 300 400]
```


### map（連想配列）
mapの基本的な書き方は下記の通り。

```go
package main

import "fmt"

func main() {
  m := map[string]int{"apple": 100, "banana": 200}
  fmt.Println(m)
}
// map[apple:100 banana:200]
```

#### mapをキーを指定して取り出し

```go
// 省略func main() {
  m := map[string]int{"apple": 100, "banana": 200}

  // キーを指定してvalueを取り出す
  fmt.Println(m["apple"])

  // banana のvalueを 300 で上書き
  m["banana"] = 300
  fmt.Println(m)
}
// 100
// map[apple:100 banana:300]
```

#### mapに追加する

```go
// 省略
func main() {
  m := map[string]int{"apple": 100, "banana": 200}

  // 追加する
  m["new"] = 500
  fmt.Println(m)
}
// map[apple:100 banana:300 new:500]
```

#### mapで存在しないキーを使った場合
存在しないキーを指定した場合は`0`が返る。
mapの宣言の際、２つ目の返り値を指定すると、値が存在するかどうかbool型で確かめることができる。

```go
// 省略
func main() {
  m := map[string]int{"apple": 100, "banana": 200}

  // 存在しないキーを指定
  fmt.Println(m["nothing"])

  // 2つ目の返り値を指定
  v, ok := m["apple"]
  fmt.Println(v, ok)

}
// 0
// 100 true
```

#### makeを使ったmapの初期化
`make`を使うと、空のmapを作成できる。
下記では空のmapを`:=`で定義した。

```go
func main() {
  m := make(map[string]int)
  m["pc"] = 5000
  fmt.Println(m)
}
// m["pc"] = 5000
```

対して`var`で同じように定義すると、`panic`というエラーが起きる。

```go
func main() {
  var m map[string]int
  m["pc"] = 5000
  fmt.Println(m)
}
// panic: assignment to entry in nil map
```

これは宣言はしているものの、メモリー上に入れるmapがないのでエラーとなる。



以上、Goの色々な定義の仕方をまとめてみました。
定義編はここまでにします。
また後日、続きを書きたいと思います。