Closed9
Golang まとめ
Goにおける型
組み込み型
種類 | 型名 |
---|---|
整数 | int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, byte, rune |
浮動小数点 | float32, float64 |
複素数 | complex64, complex128 |
文字列 | strin |
真偽値 | bool |
エラー | error |
コンポジット型
コンポジット型とは複数のデータを1つの集合として表す型になります。
構造体
0個以上の変数を集合させたデータ構造
構造体内の変数のことをフィールドと呼ぶ
// フィールドがない構造体
var empty struct{}
// フィールド3つ
var point struct {
ID string
x, y int
}
配列
同じ型のデータを並べたデータ構造
固定長のため、データの追加ができません。
// ゼロ値で初期化
var array [5]int
// 要素を定義
nums := [3]int{1, 2, 3}
// 要素数から配列数を推論
numbers := [...]int{1, 2, 3, 4, 5}
スライス
同じ型のデータを並べたデータ構造
可変長!
var slice []int
nums := []int{1,2,3}
マップ
キーと値を組み合わせたデータ構造
var m map[string]int
user := map[string]int {
"hoge": 23,
"fuga": 12,
}
スライスの操作
len, capを用いることでスライスの長さや容量を取得することができる。
長さ4, 容量:4 のスライスに対してアペンドすると、長さ: 5, 容量: 8になっていることがわかる。
容量が足りなくなると、基本的に2倍の容量のメモリ領域を再確保する。
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4}
// [1 2 3 4]
fmt.Println(nums)
// 4
fmt.Println(len(nums))
// 4
fmt.Println(cap(nums))
nums = append(nums, 5)
// [1 2 3 4 5]
fmt.Println(nums)
// 5
fmt.Println(len(nums))
// 8
fmt.Println(cap(nums))
}
makeを使ったスライスの初期化
長さと容量が指定できて便利。
package main
import "fmt"
func main() {
// len: 2, cap: 3
slice := make([]int, 2, 3)
// [0, 0]
fmt.Println(slice)
// 2
fmt.Println(len(slice))
// 3
fmt.Println(cap(slice))
}
sliceのn番目の要素を削除
appendを用いて i番目までの要素と i+1番目からの要素をくっつけたものを作成すると削除となります。
(削除ですが少し面倒ですね...)
package main
import "fmt"
func main() {
nums := []int{0, 1, 2, 3, 4, 5}
i := 2
// [0, 1]
fmt.Println(nums[:i])
// [3, 4, 5]
fmt.Println(nums[i+1:])
result := append(nums[:i], nums[i+1:]...)
// [0, 1, 3, 4, 5]
fmt.Println(result)
}
...
はスライスを展開して引数に渡しています。
https://golang.org/ref/spec#Passing_arguments_to_..._parameters
スライスを逆順に並べる
package main
import "fmt"
func main() {
nums := []int{0, 1, 2, 3, 4, 5}
for i := len(nums)/2 - 1; i >= 0; i-- {
opp := len(nums) - 1 - i
nums[i], nums[opp] = nums[opp], nums[i]
}
// [5, 4, 3, 2, 1, 0]
fmt.Println(nums)
}
スライス中央の値から
左と右を交換していく感じで面白いですね
マップの操作
値の初期化
empty := map[string]int{}
mapMake := make(map[string]int)
mapCap := make(map[string]int, 10)
key, valueの存在有無取得
他の言語で言う、 map.contains()
みたいなものも同じ書き方で取得できます。
シンプルで良いですね..!
m := map[string]int{
"test1": 11,
"test2": 22,
}
test1Num := m["test1"]
// 11
fmt.Println(test1Num)
test1Num, ok := m["test1"]
if ok {
// true
fmt.Println(ok)
// 11
fmt.Println(test1Num)
}
_, ok = m["hogehoge"]
// false
fmt.Println(ok)
マップを用いてユニークなスライスを生成
マップはキーをセットとして保持する特性があるため、
重複チェックに使えたりします。
空の構造体を用いることでメモリ要領を取りません。
A struct{} takes up no space.
ユニークなスライスを生成。
package main
import (
"fmt"
)
func main() {
nums := []int{1, 2, 3, 4, 4, 5, 5, 6}
unique := make([]int, 0, len(nums))
tmpMap := make(map[int]struct{})
for _, v := range nums {
if _, ok := tmpMap[v]; ok {
continue
}
unique = append(unique, v)
tmpMap[v] = struct{}{}
}
// [1, 2, 3, 4, 5, 6]
fmt.Println(unique)
}
レシーバ
メソッド: レシーバと紐づけられている関数のこと
レシーバ: メソッドに紐づけられている変数
レシーバを型にするかポインタにするかで挙動が変わります。
いわゆる値渡し or 参照渡し です。
package main
import "fmt"
type Counter struct {
Num int
}
func (c *Counter) Count() {
c.Num++
}
func (c Counter) CountDryRun() {
c.Num++
}
func main() {
counter := &Counter{
Num: 0,
}
// 0
fmt.Println(counter.Num)
counter.Count()
// 1
fmt.Println(counter.Num)
counter.CountDryRun()
counter.CountDryRun()
// 1
fmt.Println(counter.Num)
}
どちらにするかはざっくり以下の3つを考えると良さそうです。
- 呼び出し元の値を変える必要があるなら、ポインタ
- レシーバが大きければ、ポインタ
- レシーバが小さければ、値
このスクラップは2023/09/10にクローズされました