【自己学習用】はじめてのGo2

#基本的な文法の振り返り
####mainパッケージ
プログラムをコンパイルして実行すると，まずmainパッケージの中にあるmain()関数が実行される。

```
package main

func main() {
}
```

####インポート
インポートしたパッケージ内へは，パッケージ名にドットを付けてアクセスできる。

```
package main

import (
    "fmt"
    "github.com/wdpress/gosample"
    "strings"
)
func main() {
    fmt.Println("hello world")
}
```

####オプションの指定
任意の名前（上記ではf）を指定した場合はfで使用可能。
_を付けた場合は使用しないの意。
.を付けた場合は，使用時にパッケージ名が省略可能。

```
package main

import (
    f "fmt"
    _ "github.com/wdpress/gosample"
    . "strings"
)

func main() {
    // fmt.Println()がf.Println()になり
    // strings.ToUpper()がToUpper()なっている
    f.Println(ToUpper("hello world"))
}
```

# 変数

```
//var message string = "hello world"
//var foo, bar, buz string = "foo", "bar", "buz"

func main() {
    // どちらの書き方も同じ意味
    // var message string = "hello world"
    message := "hello world"
    fmt.Println(message)

    const Hello string = "hello"
    Hello = "bye" // cannot assign to Hello
}
```

変数の宣言はvarから。JSと似ている。
複数の宣言ができるところも似てる。
varと変数の型は省略可能。
型は推論。ここは似なくてもいいのに・・・

# if

```
func main() {
    a, b := 10, 100
    if a > b {
        fmt.Println("a is larger than b")
    } else if a < b {
        fmt.Println("a is smaller than b")
    } else {
        fmt.Println("a equals b")
    }
}

if n == 10
    fmt.Println(n)
    //これはエラー
n == 10 ? fmt.Println(n): fmt.Println(0)
//これもエラー
```

ifに丸括弧は不要。個人的には欲しい。
波括弧の省略はエラー。
ifの省略もエラー。:thumbsup:

# for

```
func main() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
    for {  //無限ループ
        doSomething()
    }
}
```

forも丸括弧は不要。
whileはなし。

# switch

```
func main() {
    n := 3
    switch n {
    case 3:
        n = n - 1
        fallthrough
    case 2:
        n = n - 1
        fallthrough
    case 1:
        n = n - 1
        fmt.Println(n) // 0
    }
}
```

goのswitchはcaseが終わったら抜けるのでbreakを書く必要はない。
続けたい場合はfallthrough。

# 関数

```
func sum(i, j int) { // func sum(i int, j int) と同じ
    fmt.Println(i + j)
}
```

引数の型が同じなら最後にまとめることができる。

```
func swap(i, j int) (int, int) {
    return j, i
}

func main() {
    x, y := 3, 4
    x, y = swap(x, y)
    fmt.Println(x, y) // 4, 3

    x = swap(x, y) // コンパイルエラー

    x, _ = swap(x, y) // 第二戻り値を無視
    fmt.Println(x) // 3

    swap(x, y) // コンパイル，実行ともに可能
}
```

関数は複数の値を返すことができる。
戻り値の数があってないとコンパイルエラーだが
_とすれば明示的に無視することができる。


# エラーを返す関数

```
func main() {
    file, err := os.Open("hello.go")
    if err != nil {
        // エラー処理
        // returnなどで処理を別の場所に抜ける
    }
    // fileを用いた処理
}
```

エラーでない場合、nil。
自作のエラーはerrorsパッケージを使う。
エラーを最後にする慣習があるため、自作APIもそれに従う。

```
package main

import (
    "errors"
    "fmt"
    "log"
)

func div(i, j int) (int, error) {
    if j == 0 {
        // 自作のエラーを返す
        return 0, errors.New("divied by zero")
    }
    return i / j, nil
}

func main() {
    n, err := div(10, 0)
    if err != nil {
        // エラーを出力しプログラムを終了する
        log.Fatal(err)
    }
    fmt.Println(n)
}
```

# 名前付き戻り値

```
func div(i, j int) (result int, err error) {
    if j == 0 {
        err = errors.New("divied by zero")
        return // return 0, errと同じ
    }
    result = i / j
    return // return result, nilと同じ
}
```

戻り値に名前を付けることができ、ゼロ値で初期化された変数として扱われる。
この場合、returnのあとに返す値を明示する必要がない。

# 関数リテラル

無名関数が作れる。
すぐに実行する関数を作る場合は以下。

```
func main() {
    func(i, j int) {
        fmt.Println(i + j)
    }(2, 4)
}
```

関数を変数に代入可能。
これもJSと同じ。

```
var sum func(i, j int) = func(i, j int) {
    fmt.Println(i + j)
}

func main() {
    sum(2, 4)
}
```

# 配列

goの配列は固定長。
関数に配列を渡す場合は値渡しとなる。

```
func fn(arr [4]string) {
    arr[0] = "x"
    fmt.Println(arr) // [x b c d]
}

func main() {
    arr := [4]string{"a", "b", "c", "d"}
    fn(arr)
    fmt.Println(arr) // [a b c d]
}
```


# スライス

スライス=可変長配列。
for文の中でrangeを用いると，添字と値の両方が取得できる。

```
s1 := []string{"a", "b"}
s2 := []string{"c", "d"}
s1 = append(s1, s2...) // s1にs2を追加
fmt.Println(s1)        // [a b c d]

var arr [4]string

arr[0] = "a"
arr[1] = "b"
arr[2] = "c"
arr[3] = "d"

for i, s := range arr {
    // i = 添字, s = 値
    fmt.Println(i, s)
}
```

始点と終点をコロンで挟んで指定すると，その範囲の値を切り出すことができる。
始点，終点を省略した場合，それぞれ先頭，末尾となる。

```
s := []int{0, 1, 2, 3, 4, 5}
fmt.Println(s[2:4])      // [2 3]
fmt.Println(s[0:len(s)]) // [0 1 2 3 4 5]
fmt.Println(s[:3])       // [0 1 2]
fmt.Println(s[3:])       // [3 4 5]
fmt.Println(s[:])        // [0 1 2 3 4 5]
```

# マップ

```
//var month map[int]string = map[int]string{}

month := map[int]string{
    1: "January",
    2: "February",
}
fmt.Println(month) // map[1:January 2:February]
```

マップからキーと取り出す場合。
キーがあるかを調べる場合は二つ目の引数で判断できる。（boolで返却）

```
jan := month[1]
fmt.Println(jan) // January

_, ok := month[1]
if ok {
    // データがあった場合
}
```

スライス同様，rangeを使用可能。


# defer

ファイル操作などを行う場合，使用後のファイルは必ず閉じる必要がある。
途中で抜けたりして閉じられない可能性が出てくると困る処理はdeferを付ける。
下記ではmain()を抜ける直前に必ず実行されるようになる。

```
func main() {
    file, err := os.Open("./error.go")
    if err != nil {
        // エラー処理
    }
    // 関数を抜ける前に必ず実行される
    defer file.Close()
    // 正常処理
}
```

# パニック

戻り値で表現できないエラーはパニックという方法でエラーが発生する。

```
func main() {
    defer func() {
        err := recover()
        if err != nil {
            // runtime error: index out of range
            log.Fatal(err)
        }
    }()

    a := []int{1, 2, 3}
    fmt.Println(a[10]) // パニックが発生
}
```

このパニックで発生したエラーはrecover()という組込み関数で取得し，そこでエラー処理を実施できる。
recover()をdeferの中に書くことで，パニックで発生したエラーの処理を実施してから，関数を抜けることができる。

パニックは組込み関数panic()を用いて自分で発生させることもできる。

```
a := []int{1, 2, 3}
for i := 0; i < 10; i++ {
    if i >= len(a) {
        panic(errors.New("index out of range"))
    }
    fmt.Println(a[i])
}
```

ただしパニックを用いるのは，エラーを戻り値として表現できない場合や，回復が不可能なシステムエラー，やむを得ず大域脱出が必要な場合など。
基本は戻り値で表現する。