🏃‍♂️

A Tour of Goをやったのでまとめ

2024/04/28に公開

タイトル通り、A Tour of Goをやったので、メモとしてまとめてみました。
ドキュメントの内容を箇条書きで簡単にまとめたので、詳細は本ドキュメントを読んで欲しいです。
自分が大事かなと思ったところをピックアップして記載しているので、全ての内容は網羅していませんので、ご了承ください。

Exported names

  • 最初の文字が大文字で始まる名前は、外部のパッケージから参照できるエクスポート(公開)された名前( exported name )。 例えば、 Pi は math パッケージでエクスポートされている。
  • 小文字ではじまる pi や hoge などはエクスポートされていない名前。
  • パッケージをインポートすると、そのパッケージがエクスポートしている名前を参照することができる。 エクスポートされていない名前(小文字ではじまる名前)は、外部のパッケージからアクセスすることはできない。

Functions

  • 引数の型は変数名の後ろにつける
  • 2つ以上の引数が同じ型である場合は、最後の型だけ残して省略できる
package main
import "fmt"

func add(x int, y int) int {
	return x + y
}

func add2(a, b int) int {
	return a + b
}

func main() {
	fmt.Println(add(1,2))
	fmt.Println(add2(3,6))
}
  • 複数の戻り値を返すこともできる
package main

import "fmt"

func swap(x, y string)(string,string){
	return x, y
}

func main() {
	a, b := swap("Hello", "Tanaka")
	fmt.Println(a, b)
}

Named return values

  • 戻り値となる変数に名前をつけることができる
  • 関数の最初で定義した変数名として扱われる
  • returnステートメントに何も書かずに戻すことができる(naked return)
    • 短い関数でのみ利用するのが好ましい(長い関数だと読みやすさに欠けてしまう)
package main

import "fmt"

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

func main() {
	fmt.Println(split(17))
}

Variables

  • varステートメントで変数宣言
    • var宣言の代わりに:= の代入文を使って、暗黙的な宣言ができる。
    • 関数の外では暗黙的な宣言はできないので、通常通りvarキーワード必要
  • 関数の引数同様、複数の変数の最後に型を書くことで、変数のリストを宣言
package main

import "fmt"

var c, python, java bool = true, false, false

func main() {
	p := 9 // 関数の中なのでOK
	fmt.Println(p, c, python, java)
}

Zero values

  • 変数に初期値を与えずに宣言すると、ゼロ値が与えられる
  • 型によって与えられる値が異なる。以下参照
    • 数値(int, floatなど) = 0
    • bool = false
    • string = “”(空文字列)

Type inference

  • 明示的な型を指定せずに変数を宣言すると、変数の右側の変数から型推論する
package main

import "fmt"

func main() {
	v := "tanaka"
	fmt.Printf("v is of type %T\n", v) // v is of type string
}

Constants

  • 定数はconstキーワードを使う
  • 宣言の仕方は変数と同じであるが、:= 使っての宣言はできない
  • 文字列、boolean、numericのみで使える

For

  • セミコロン(;)で以下の3つに分ける
    • 初期化ステートメント:  最初のイテレーション(繰り返し)前に初期化する。
    • 条件式: イテレーションごとに評価。falseとなった時イテレーション停止する。
    • 後処理ステートメント: イテレーション毎の最後に実行される。
  • 初期化と後処理ステートメントの記述は任意
  • セミコロンも省略できる
  • JavaScript等で必要な括弧()は必要ない
package main

import "fmt"

func main() {
	sum := 0
	
	// i = 0で初期化、iが10未満の間実行、iを1ずつ加算
	for i := 0; i < 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}

// 初期化と後処理とセミコロン省略
package main

import "fmt"

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

IF

  • for文同様に括弧()は必要ない
  • forのように、条件前に評価するための簡単なステートメントを描くこともできる
  • 通常通りif-elseもできる

Switch

  • 選択されたcase文だけを実行して、それに続く全てのcaseは実行されない
  • Java、C++等にはbreakがあるがGoでは自動で提供される
  • Goではcaseの値は定数である必要はない
  • 条件のないswitchは、switch trueと同じ
    • if-then-elseをシンプルにできる
package main

import (
 "fmt"
 "runtime"
)

func main() {
	fmt.Println("Go runs on")
	
	switch os := runtime.GOOS; os {
		case "darwin":
			fmt.Println("OS X.")
		case "linux":
			fmt.Println("Linux.")
		default:
			fmt.Println("%s.\n",os)
		}
}

Defer

  • deferステートメントは、deferへ渡した関数の実行を、呼び出し元の関数の終わりまで遅延させるもの
  • すぐ評価されるが、関数自体は呼び出しもtの関数がreturnするまで実行されない
  • 複数ある場合、スタックされる
package main

import "fmt"

func main() {
	defer fmt.Println("Hello Go language!")
	
	fmt.Println("Let's enjoy!")
}

Pointer

  • ポインタは値のメモリアドレスを指す
  • 変数Tのポインタは*T型でゼロ値はnil
  • &オペレータは、そのオペランドへのポインタを引き出す
  • *オペレータは、ポインタの指す先の変数を示す
package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // point to i
	fmt.Println(*p) // read i through the pointer
	*p = 21         // set i through the pointer
	fmt.Println(i)  // see the new value of i

	p = &j         // point to j
	*p = *p / 37   // divide j through the pointer
	fmt.Println(j) // see the new value of j
}

Structs

  • フィールドの集まり
  • ドット(.)でフィールドにアクセスする
package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	vertex := Vertex{1, 2}
	fmt.Println(vertex.Y)
}
  • ポインタを通してもフィールドにアクセスできる
package main

import "fmt"

type Vertex struct {
	X int
	Y int
}

func main() {
	vertex := Vertex{1, 2}
	p := & vertex
	p.X = 1e9
	fmt.Println(vertex)
}

Struct Literals

  • structリテラルは、フィールドの値を列挙することで新しいstructの初期値を割り当てることができる。
package main

import "fmt"

type Vertex struct {
	X, Y int
}

var (
	v1 = Vertex{1, 2}  // has type Vertex
	v2 = Vertex{X: 1}  // Y:0 is implicit
	v3 = Vertex{}      // X:0 and Y:0
	p  = &Vertex{1, 2} // has type *Vertex
)

func main() {
	fmt.Println(v1, p, v2, v3)
}

Arrays

  • 配列のこと
  • var a [10] intだとint型を型に持つ10個の配列を指している
  • 固定長なので配列のサイズを変えることができない
package main

import "fmt"

func main() {
	var names [3] string
	names[0] = "Tanaka"
	names[1] = "Sato"
	names[2] = "Suzuki"
	fmt.Println(names[0])
	fmt.Println(names)
	
	greetings := [3]string{"Hello", "Good night", "See you"}
	fmt.Println(greetings[1])
	fmt.Println(greetings)
}

Slices

  • 可変長な配列と見なすことができる
  • 配列よりも一般的(らしい)
package main

import "fmt"

func main() {
	primes := [6]int{2, 3, 5, 7, 11, 13}

	var s []int = primes[1:4] // primesの要素内のprimes[1]~primes[3]を含むスライスを作る
	fmt.Println(s)
}

  • スライスは配列への参照のようなもの
  • スライスの要素を変更すると、元となる配列の対応する要素も変更される
  • 同じ元となる配列を共有している他のスライスは、それらの変更が反映される
package main

import "fmt"

func main() {
	primes := [6]int{2, 3, 5, 7, 11, 13}

	var s []int = primes[1:4] // primesの要素内のprimes[1]~primes[3]を含むスライスを作る
	fmt.Println(primes)  // [2 3 5 7 11 13]
	s[0] = 100
	fmt.Println(primes) // [2 100 5 7 11 13]
}

  • スライスのリテラルは長さのない配列リテラルのようなもの
    • [3]bool{true,true,false} 配列リテラル
  • 上機と同様の配列を作成し、それを参照するスライスは以下となる
    • []bool{true.true,false}

Creating a slice with make

  • スライスは組込みのmake関数を使用して作成可能
    • 動的サイズの配列を作成する方法となる
  • make関数はゼロ化された配列を割り当て、その配列を指すスライスを返す
package main

import "fmt"

func main() {
	a := make([]int, 5)
	printSlice("a", a)

	b := make([]int, 0, 5)
	printSlice("b", b)

	c := b[:2]
	printSlice("c", c)

	d := c[2:5]
	printSlice("d", d)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n",
		s, len(x), cap(x), x)
}

Appending to a slice

  • スライスに新しい要素を追加するためにはappend を使う。
package main

import "fmt"

func main() {
	var n []string
	printSlice(s)

	// append works on nil slices.
	n = append(n, "Tanaka")
	printSlice(n)

	// The slice grows as needed.
	n = append(n, "Sato")
	printSlice(n)

	// We can add more than one element at a time.
	n = append(n, "Suzuki","Kawamura")
	printSlice(n)
}

func printSlice(n []string) {
	fmt.Printf("len=%d cap=%d %v\n", len(n), cap(n), n)
}

Maps

  • mapはキーと値を紐づける
  • マップのゼロ値はnil
    • nilマップはキーを持っておらず、キーを追加することもできない
package main

import "fmt"

type Place struct {
	name, address string
}

var m map[string]Place

func main(){
	m = make(map[string]Place)
	m["A shop"] = Place{
		"Aショップ渋谷店", "渋谷区0-0-0",
	}
	
	fmt.Println(m["A shop"])
}

Mutating Maps

  • 要素の挿入と更新
m[key] = elem
  • 要素の取得
elem = m[key]
  • 要素の削除
delete(m, key)
  • キーに対する要素が存在するかどうかの確認
  • keyが存在すれば変数okはtrue、存在しなければfalseとなる
elem, ok = m[key]

参考

A Tour of Go

Discussion