Open28

go 入門

syysyy

import

複数まとめてimportできる。カンマは不要。

import (
    "fmt"
    "math/rand"
)

他の言語のように都度import書くことも可能。まとめてimportの方が推奨らしい

import "fmt"
import "math"
syysyy

公開/非公開

大文字で定義した変数/関数は外部のパッケージから参照可能になる。小文字は参照されない。

        // OK
	fmt.Println(math.Pi)
        // NG
	fmt.Println(math.pi)
syysyy

関数

よくある感じ。変数の後ろに型を指定する

func hoge() int {
	return 1
}

func hoge(x int) int {
	return x * 2
}

// 同じ型が続く場合、まとめて定義可能
func add2(x, y int) int {
	return x + y
}

func add2(x, y int, a int) int {
	return x + y + a
}

// 複数の値をreturnすることも可能
func hoge(x, y string) (string, string) {
	return y, x
}
syysyy

Named return values

関数の戻り値の変数名を予め指定することも可能。
指定した変数はすでに宣言済みとして扱われるので y := x + 1 にするとエラーになるので注意
最後のreturnは必須

func hoge(x int) (y int) {
	y = x + 1
	return
}

func hoge2(x, y int) (sum int) {
	sum = x + y
	return
}
syysyy

変数宣言
varを使う。変数名の後に型が来る。変数に値を入れない場合はデフォルト値が使用される

var hoge, fuga int

func aaa() {
	var piyo int
	fmt.Println(hoge, fuga, piyo)
}
syysyy

変数宣言 + 初期化

型の違う変数もまとめて定義できるのすごい

func hoge() {
	var i = 1
	var hoge, piyo, fuga = 1, "aaaa", false
	fmt.Println(i, hoge, piyo, fuga)
}

// 1 1 aaaa false
syysyy

変数宣言 + :=

:=を使えばvarは不要。これは関数内でしか使用できない。関数外ではvarが必須

func hoge() {
	i := 1
	hoge, fuga, piyo := 1, "aaa", false
	fmt.Println(i, hoge, fuga, piyo)
}
syysyy

定数

const を使う。:=による宣言は使用不可

const a = 1
syysyy

for

他の言語にあるようなfor文やwhile文がかける

	for i := 0; i < 10; i++ {
		sum += 1
	}

	for sum < 100 {
		sum += 10
	}
syysyy

無限ループ
ループ条件を書かなければ無限ループにできる

for {
}
syysyy

if

他の言語と同じ書き方

if true {
  // any
} else {

}

こんな感じでif文内でのみ使用可能な変数を宣言することも可能

	if x := isA(); x {
		fmt.Println("hogeeee")
	}
syysyy

switch
breakなしでok。マッチしたcaseのみ実行される。
条件を書かない場合は一番最初にマッチしたcaseのみ実行される

	switch hoge := 2; hoge {
	case 1:
		fmt.Println(1)
	case 2:
		fmt.Println(2)
	default:
		fmt.Println("default")
	}

// 条件を書かなくてもok
switch {
case 1 < 1:
	fmt.Println("aaaa")
case 1 < 2:
	fmt.Println("bbb")
}
syysyy

defer

関数終了後に行う後処理
bbb -> aaaの順で出力される

func main() {
	defer fmt.Println("aaa")
	fmt.Println("bbb")
}

deferが複数ある場合はLIFOで実行される。
start -> fin -> 9 .... 1で出力される

func main() {
	fmt.Println("start")

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

	fmt.Println("fin")
}
syysyy

ポインタ

&変数名で変数のポインタを取得。
ポインタの型は*Tになる。
*Tでポインタが参照する変数にアクセスできる

func main() {
	i := 1
	j := &i
	*j = 2
	fmt.Println(i, *j)
}

// 2 2
syysyy

struct

type 構造体名 struct { フィールド }で構造体を定義できる
構造体名{ 初期値 }で初期値を設定できる

type hoge struct {
    x, y int
}

func main () {
    fmt.Println(hoge{1,2})
}

ドットでフィールドにアクセスできる

type Hoge struct {
	X, Y int
}

func main() {
	hoge := Hoge{1, 2}
	hoge.X = 10
	fmt.Println(hoge.X)
}

ポインタ経由でのアクセス
(*p).Xでフィールドにアクセスできるが、単純にp.Xでもアクセス可能

type Hoge struct {
	X, Y int
}

func main() {
	hoge := Hoge{1, 2}
	fuga := &hoge
	fuga.X = 999
	fmt.Println(fuga)
	fmt.Println(*fuga)
}

// &{888 2}
// {888 2}

フィールド名を指定することで任意のフィールドのみ初期値を設定することもできる

	hoge = Vertex{1, 2}
	fuga = Vertex{X: 1}
	piyo = Vertex{}
	a = &Vertex{}
syysyy

配列

固定数配列。要素数も含めて配列の型となる。
配列を別の変数に入れるとコピーされる。

func main() {
	a := [2]int{1,2}
	fmt.Println(a)
	
	var b [2]string
	b[0] = "a"
	b[1] = "b"
	fmt.Println(b)
	
	c := b
	c[1] = "c"
	fmt.Println(b,c)
	
	d := &b
	d[1] = "d"
	fmt.Println(b, d)
}

[1 2]
[a b]
[a b] [a c]
[a d] &[a d]
syysyy

スライス

可変長配列。

配列から作成できる。配列[low:high]
lowからhigh-lowの数だけ要素を取得する。
コピーではなく、参照が共有されるのでスライスの要素を修正したら配列も変更される

func main() {
	hoge := [5]int{0,1,2,3,4}
	fuga := hoge[2:4]
	fuga[0] = 9
	
	fmt.Println(fuga, hoge)
}
[9 3] [0 1 9 3 4]

配列作成時と似た感じでスライスを作成できる

hoge := []int{1,2,3}

範囲指定を省略可能。↓全て同じ範囲

	hoge := [3]int{0,1,2}
	fuga := hoge[:]
	fuga = hoge[0:]
	fuga = hoge[:3]
	fuga = hoge[0:3]
	fmt.Println(fuga)

makeでlenとcapを指定してスライスを作成できる

hoge := make([]int, 5)
// len = 5, cap = 5

hoge := make([]int, 5, 10)
// len = 5, cap = 10

スライス in スライスもできる

	hoge := [][]int{
		[]int{1, 2, 3},
		[]int{4, 5, 6},
	}

	for i := 0; i < len(hoge); i++ {
		fmt.Println(hoge[i])
	}
syysyy

スライスに追加

appendを使う

	var hoge []int
	hoge = append(hoge, 0)
	hoge = append(hoge, 1,2,3,4)
syysyy

スライス for
rangeで配列やスライス, mapを回せる。
indexとvalueを取得できる

	hoge := []int{0, 1, 2, 3, 4, 5}
	for i, v := range hoge {
		fmt.Println(i, v)
	}
syysyy

戻り値を使用しない場合
"_"で受け取ることで戻り値を破棄することができる

func main() {
	a := []int{1, 2, 3}
	for _, v := range a {
		fmt.Println(v)
	}
	_ = hoge()
}

func hoge() int {
	return 1
}
syysyy

map

連想配列。辞書

これでintのkeyとstringのvalueを持つmapが作成可能

func main() {
	hoge := make(map[int]string)
	hoge[0] = "a"
	hoge[1] = "b"
	hoge[999] = "c"
	fmt.Println(hoge)
}

map[0:a 1:b 999:c]

初期値指定

	hoge := map[int]string{
		0: "a",
		1: "b",
	}
	fmt.Println(hoge)
map[0:a 1:b]

要素が推測できる場合は型名を省略しても良い

type Hoge struct {
	x, y int
}

	fuga := map[string]Hoge{
		"a": {0, 1},
		"b": {2, 3}, //型名なし
		"c": Hoge{4,5}, // 型名あり
	}
	fmt.Println(fuga)
map[a:{0 1} b:{2 3}]
syysyy

map追加, 取得, 削除

存在しない要素を取得した場合はゼロ値とokが返される。
okがfalseなら存在しない要素を取得しようとしている。

	hoge := make(map[string]int)
	
	hoge["a"] = 1
	fmt.Println(hoge["a"])
	
	delete(hoge, "a")
	
	fmt.Println(hoge["a"])
	
	value, ok := hoge["a"]
	fmt.Println(value, ok)

1
0
0 false
syysyy

関数

関数を引数として受け取ることができる

func main() {
	a := func () int {
		return 1
	}
	b := hoge(a)
	fmt.Println(b)
}

func hoge(fn func() int) int {
	a := fn()
	return a + 1
}
syysyy

method

構造体にメソッドを追加できる。
関数にレシーバをつけるとメソッドになる。


type Hoge struct {
	X, Y int
}

func (hoge Hoge) Sum() int {
	return hoge.X + hoge.Y
}

func main() {
	hoge := Hoge{1, 2}
	fmt.Println(hoge.Sum())
}

任意のstructにメソッドを追加できる。
同じパッケージのstructにのみメソッドを追加できる。
intやfloat64などにメソッドを追加する場合は自分のパッケージ内で再定義する必要がある。

type MyInt int

func (i MyInt) hoge() int {
	return int(i * 2)
}

syysyy

メソッド ポイントレシーバ

レシーバとポイントレシーバー。
ポイントレシーバーで実装すると変数を変更できる
レシーバーだとメソッド実行時にコピーが発生する。

func (hoge Hoge) plus() {
// any
}

func (hoge *Hoge) plus() {
// any
}
syysyy

empty interface

空のインターフェースには何の型でも入れられる。

var i interface{}
syysyy

type assertions

	var hoge interface{} = "1111"
	
	if a, ok := hoge.(int); ok {
		fmt.Println(a, ok)
	}

	if a, ok := hoge.(string); ok {
		fmt.Println(a, ok)
	}
1111 true

switchによる条件分岐もできる

	switch h := i.(type) {
	case int:
		fmt.Println("int desu")
	case string:
		fmt.Println("string desu")
	default:
		fmt.Println("default desu")
	}