Go
golang

Go言語初心者が学んだ基礎的な文法まとめ

More than 1 year has passed since last update.

Go言語の型の種類

こんにちは。僕もGoを勉強し始めることになりました。
学んだ文法をまとめたいと思います。

変数

型を宣言して使う

//型を宣言する
var str string
str = "hello"
fmt.Println(str)//=>hello
fmt.Println(reflect.TypeOf(str))//=>string

型推論

//型は推論される
var str = "hello"
fmt.Println(str) //=>hello
fmt.Println(reflect.TypeOf(str)) //=>string

varを省略する

//変数宣言と初期化を行う場合に、var と型宣言を省略して「:=」演算子を用いることができる。
str := "hello"
fmt.Println(str)//=>hello
fmt.Println(reflect.TypeOf(str))//=>string

型の種類

主にこんな感じ。

int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex64 complex128
bool byte
string rune

型の変換

型の変換には"strconv"を使用する。

文字列から数値に変換する

import (
    "fmt"
    "strconv"
    "reflect"
)

func main() {
    var i int
    i, _ = strconv.Atoi("100")
    fmt.Println(i) //=>100
    fmt.Println(reflect.TypeOf(i)) //=>int

}

数値から文字列に変換する

import (
    "fmt"
    "strconv"
    "reflect"
)

func main() {
fmt.Println(strconv.Itoa(100)) //=>100
fmt.Println(reflect.TypeOf(strconv.Itoa(100))) //=>string
}

interface型

全ての型と互換性を持っているinterface{}型

var obj interface{}

obj = 123                                                              // int
obj = "str"                                                            // string
obj = []string{"linux", "windows", "android"}                          // slice
obj = make(chan string)                                                // channel
obj = func(val int) string { return fmt.Sprintf("number is %d", val) } // function

Go言語 - 空インターフェースと型アサーション

ポインタ型

func callByValue(i int) {
    i = 20 // 値を上書きする
}

func callByRef(i *int) {
    *i = 20 // 参照先を上書きする
}

func main() {
    var i int = 10
    callByValue(i) // 値を渡す
    fmt.Println(i) // 10
    callByRef(&i) // アドレスを渡す
    fmt.Println(i) // 20
}

はじめてのGo―シンプルな言語仕様,型システム,並行処理

ポインタ型とそうではない型の違いは、構造体の部分で書きます。

型の確認

型の確認には、reflectを使う。

package main

import (
    "fmt"
    "reflect"
)

func main() {
    fmt.Println(reflect.TypeOf(1))   //=>int
    fmt.Println(reflect.TypeOf("test"))   //=>string
    fmt.Println(reflect.TypeOf([]string{"a", "b", "c"}))  //=>[]string
}

配列

型とサイズを決めて配列を宣言。

var arr [5]int
//5つのものが入る配列arrを宣言
//6以上入るとエラー。
var arr [5]int
arr[5] = 5
fmt.Println(arr) //=>invalid array index 5 (out of bounds for 5-element array)

宣言した数以上のものを入れることができない。
柔軟に対応するためには、どうするか=>sliceを使う。

slice

配列は入れることのできる数が決まっているが、sliceは決まっていない。可変長である。

sliceの定義方法

a:=[]T{}
a:=make([]T,len)

sliceの使い方

fruits := []string{"apple", "orange", "lemon"}
fmt.Println(fruits) // => [apple orange lemon]
fruits = append(fruits, "peach")
//追加の際には、appendを使う。
fmt.Println(fruits) //=>[apple orange lemon peach]
//peachが追加された。

map(連想配列)

Golangではハッシュ(連想配列)のことをmapと呼ぶ。

package main

import (
    "fmt"
)

func main() {
    m := map[string]int{"japanese": 68, "english": 70, "math": 90}
    fmt.Println(m) //=>map[japanese:68 english:70 math:90]
    fmt.Println(m["japanese"]) //=>68
    //mapからキーの値を取り出す
    m["science"] = 100
    //要素を追加する
    fmt.Println(m) //=>map[japanese:68 english:70 math:90 science:100]
}

構造体

struct定義

structを使ってそのフィールドが持つ値を決定することができる。

type Animal struct {
    Name string
    Age  int
}

クラスのようなもの。

structを初期化する

初期化の方法にもいくつかあるので、見ていきます。

newを使う

さっき作ったAnimalを初期化します。初期化したanimalに値を入れます。

package main

import "fmt"

type Animal struct {
    Name string
    Age  int
}

func main() {
    animal := new(Animal)//:= でポインタ型
    animal.Name = "Tanaka"//NameにTanakaを代入
    animal.Age = 10//Ageに10を代入

    fmt.Println(animal)
    //=>&{Tanaka 10}
}

ポインタ型でTanakaと10というものが返ってきます。

ポインタ型で初期化する

package main

import "fmt"

type Animal struct {
    Name string
    Age  int
}

func main() {
    animal := &Animal{
        Name: "Tanaka",
        Age: 10,
    }
    fmt.Println(animal)
    //=>&{Tanaka 10}
}

これでも同じ結果です。

そのまま初期化して使う

package main

import "fmt"

type Animal struct {
    Name string
    Age  int
}

func main() {
    animal := Animal{
        Name: "Tanaka",
        Age: 10,
    }
    fmt.Println(animal)
    //=>{Tanaka 10}
}

今回はポインタ型ではなく、そのまま出力されました。

構造体の初期化方法で何が違うのか

これは一体何が違うのでしょう。

以下の記事に違いが書かれていました。引数として使った場合などに変化が見られるそうです。
https://qiita.com/kmtr/items/1e7caf92aa8bb587906d

試してみます。

package main

import "fmt"

type Animal struct {
    Name string
    Age  int
}

func changeAge1(animal1 Animal, age int) {
    animal1.Age = age
    fmt.Printf("%p\n", &animal1)
}

func changeAge2(animal2 *Animal, age int) {
    animal2.Age = age
    fmt.Printf("%p\n", animal2)
}

func main() {

    animal1 := Animal{
        Name: "Tanaka",
        Age: 10,
    }
    fmt.Printf("%p\n", &animal1)
    changeAge1(animal1, 100)

    animal2 := &Animal{
        Name: "Tanaka",
        Age: 10,
    }

    fmt.Printf("%p\n", animal2)
    changeAge2(animal2, 100)
}

結果

0xc42000a060
0xc42000a080
0xc42000a0a0
0xc42000a0a0

なるほど、確かに以下のように構造体を初期化した場合は、引数として使うと、構造体の値を変更した際にポインタが変わってしまうようです。

    animal1 := Animal{
        Name: "Tanaka",
        Age: 10,
    }

構造体の初期化方法というよりポインタの扱い方な気がします。
Goのポインタが参考になります。

for文

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}

if文

package main

import (
    "fmt"
    "math"
)

func sqrt(x float64) string {
    if x < 0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

func main() {
    fmt.Println(sqrt(2), sqrt(-4))
}

switch文

package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os {
    case "darwin":
        fmt.Println("OS X.")
    case "linux":
        fmt.Println("Linux.")
    default:
        // freebsd, openbsd,
        // plan9, windows...
        fmt.Printf("%s.", os)
    }
}

終わりに

Go言語触ってみましたが、正直まだまだ見えてきません。
いつか見えてくる日がくるのだろうか。その時を待ちたいと思う。