go 入門
A tour of Go
doc
import
複数まとめてimportできる。カンマは不要。
import (
"fmt"
"math/rand"
)
他の言語のように都度import書くことも可能。まとめてimportの方が推奨らしい
import "fmt"
import "math"
公開/非公開
大文字で定義した変数/関数は外部のパッケージから参照可能になる。小文字は参照されない。
// OK
fmt.Println(math.Pi)
// NG
fmt.Println(math.pi)
関数
よくある感じ。変数の後ろに型を指定する
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
}
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
}
変数宣言
varを使う。変数名の後に型が来る。変数に値を入れない場合はデフォルト値が使用される
var hoge, fuga int
func aaa() {
var piyo int
fmt.Println(hoge, fuga, piyo)
}
変数宣言 + 初期化
型の違う変数もまとめて定義できるのすごい
func hoge() {
var i = 1
var hoge, piyo, fuga = 1, "aaaa", false
fmt.Println(i, hoge, piyo, fuga)
}
// 1 1 aaaa false
変数宣言 + :=
:=を使えばvarは不要。これは関数内でしか使用できない。関数外ではvarが必須
func hoge() {
i := 1
hoge, fuga, piyo := 1, "aaa", false
fmt.Println(i, hoge, fuga, piyo)
}
定数
const を使う。:=による宣言は使用不可
const a = 1
for
他の言語にあるようなfor文やwhile文がかける
for i := 0; i < 10; i++ {
sum += 1
}
for sum < 100 {
sum += 10
}
無限ループ
ループ条件を書かなければ無限ループにできる
for {
}
if
他の言語と同じ書き方
if true {
// any
} else {
}
こんな感じでif文内でのみ使用可能な変数を宣言することも可能
if x := isA(); x {
fmt.Println("hogeeee")
}
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")
}
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")
}
ポインタ
&変数名で変数のポインタを取得。
ポインタの型は*Tになる。
*Tでポインタが参照する変数にアクセスできる
func main() {
i := 1
j := &i
*j = 2
fmt.Println(i, *j)
}
// 2 2
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{}
配列
固定数配列。要素数も含めて配列の型となる。
配列を別の変数に入れるとコピーされる。
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]
スライス
可変長配列。
配列から作成できる。配列[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])
}
スライスに追加
appendを使う
var hoge []int
hoge = append(hoge, 0)
hoge = append(hoge, 1,2,3,4)
スライス for
rangeで配列やスライス, mapを回せる。
indexとvalueを取得できる
hoge := []int{0, 1, 2, 3, 4, 5}
for i, v := range hoge {
fmt.Println(i, v)
}
戻り値を使用しない場合
"_"で受け取ることで戻り値を破棄することができる
func main() {
a := []int{1, 2, 3}
for _, v := range a {
fmt.Println(v)
}
_ = hoge()
}
func hoge() int {
return 1
}
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}]
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
関数
関数を引数として受け取ることができる
func main() {
a := func () int {
return 1
}
b := hoge(a)
fmt.Println(b)
}
func hoge(fn func() int) int {
a := fn()
return a + 1
}
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)
}
メソッド ポイントレシーバ
レシーバとポイントレシーバー。
ポイントレシーバーで実装すると変数を変更できる
レシーバーだとメソッド実行時にコピーが発生する。
func (hoge Hoge) plus() {
// any
}
func (hoge *Hoge) plus() {
// any
}
empty interface
空のインターフェースには何の型でも入れられる。
var i interface{}
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")
}