LoginSignup
0
0

More than 3 years have passed since last update.

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

Posted at

基本的な文法の振り返り

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])
}

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0