Skip to content

Instantly share code, notes, and snippets.

@1206yaya
Last active September 11, 2024 07:13
Show Gist options
  • Save 1206yaya/9dc552cf7ae4ce86118a091e304a3952 to your computer and use it in GitHub Desktop.
Save 1206yaya/9dc552cf7ae4ce86118a091e304a3952 to your computer and use it in GitHub Desktop.
Go Cheat Sheet #go #cheat-sheet

ChatGPT - gpts: tool cheat sheet

VSCode

Debugingz

launch.json

  • 現在開いているコードをデバックする: Debug Current Go File
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug Go Test",
      "type": "go",
      "request": "launch",
      "mode": "test",
      "program": "${fileDirname}",
      "args": ["-test.run", "Test", "-test.v"],
      "showLog": true,
      "env": {},
      "buildFlags": "-gcflags 'all=-N -l'",
      "logOutput": "rpc" // Delveのログ出力を最小限に抑える
    },
    {
      "name": "Debug Current Go File",
      "type": "go",
      "request": "launch",
      "mode": "auto",
      "program": "${file}",
      "env": {},
      // "args": ["${input:argument}"],
      "console": "integratedTerminal",
      // これがないと、ルートディレクトリで実行した時と、個別のファイルをデバックしたときの読み込みパスが同じにならない
      "cwd": "${workspaceFolder}"
    }
  ]
}

Slice

スライスは配列の要素を柔軟に操作するためのラッパーのようなものであり、動的なサイズ変更が可能なデータ構造です。 一方、配列は固定サイズでメモリ効率が良く、主に固定サイズのデータが必要な場面で使用されます。

基本的なスライスの操作

1. スライスの作成

// 既存の配列からスライスを作成
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4]  // sは[2, 3, 4]となる

// make関数を使用してスライスを作成
s := make([]int, 5)  // 長さ5のスライスを作成 [0, 0, 0, 0, 0]

2. スライスの一部を取得

// S[i:] はスライスのi番目から最後までを取得
s := []int{1, 2, 3, 4, 5}
subSlice := s[2:]  // subSliceは[3, 4, 5]となる

// S[:i] はスライスの最初からi番目までを取得
subSlice := s[:3]  // subSliceは[1, 2, 3]となる

// S[i:j] はスライスのi番目からj番目までを取得
subSlice := s[1:4]  // subSliceは[2, 3, 4]となる

3. スライスのコピー

src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)  // dstは[1, 2, 3]となる

4. スライスの追加

s := []int{1, 2, 3}
s = append(s, 4)  // sは[1, 2, 3, 4]となる

// 複数要素を末尾追加
s = append(s, 5, 6)  // sは[1, 2, 3, 4, 5, 6]となる
4.1 先頭に追加
s := []int{2, 3, 4}
s = append([]int{1}, s...) // s は[1, 2, 3, 4]となる

5. スライスの容量と長さ

s :make([]int, 0, 5) // 長さ0、容量5のスライスを作成
// []
s := make([]int, 3, 5)  // 長さ3、容量5のスライスを作成
// [0, 0, 0]

// 長さの取得
length := len(s)  // 3

// 容量の取得
capacity := cap(s)  // 5

6. スライスの要素を削除

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

// 2番目の要素(値3)を削除
s = append(s[:2], s[3:]...)  // sは[1, 2, 4, 5]となる

応用的な操作

1. スライスの逆順

s := []int{1, 2, 3, 4, 5}
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
    s[i], s[j] = s[j], s[i]
}
// sは[5, 4, 3, 2, 1]となる

2. スライスのコピーとリサイズ

s := []int{1, 2, 3}
s = append(s[:2], s[2:]...)  // sは[1, 2, 3]と変わらない

s = append(s[:2], s[3:]...)  // 2番目の要素を削除し、[1, 2]となる

3. スライス内の要素の検索

s := []int{1, 2, 3, 4, 5}
target := 3
found := -1
for i, v := range s {
    if v == target {
        found = i
        break
    }
}
// foundは2(3が見つかったインデックス)となる。見つからなければ-1。

以下に、スライスの複製に関するチートシートを、コメントで結果が分かる形式でまとめました。

スライスの複製 (Shallow Copy vs. Deep Copy)

1. 浅いコピー(Shallow Copy)

s1 := []int{1, 2, 3}
s2 := s1  // s2は[1, 2, 3]となる(s1の浅いコピー)

s2[0] = 10  // s1とs2はどちらも[10, 2, 3]となる

2. 深いコピー(Deep Copy)

s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1)  // s2は[1, 2, 3]となる(s1の深いコピー)

s2[0] = 10  // s1は[1, 2, 3]のままで、s2は[10, 2, 3]となる

スライスの複製の詳細

1. 複製後のサイズと容量

s1 := []int{1, 2, 3}
s2 := make([]int, len(s1), cap(s1)+5)
copy(s2, s1)  // s2は[1, 2, 3]となる(容量はs1より大きい)

2. 二次元スライスの深いコピー

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

s2 := make([][]int, len(s1))
for i := range s1 {
    s2[i] = make([]int, len(s1[i]))
    copy(s2[i], s1[i])  // s2の各スライスはs1の各スライスの深いコピーとなる
}
// s2は[[1, 2, 3], [4, 5, 6]]となる

3. スライスの一部を複製

s1 := []int{1, 2, 3, 4, 5}
s2 := make([]int, 2)
copy(s2, s1[1:3])  // s2は[2, 3]となる(s1の一部を深いコピー)

まとめ

  • 浅いコピー: スライスのポインタだけがコピーされ、元のデータは共有されるため、どちらかを変更するともう一方も変更されます。
  • 深いコピー: copy関数を使用して、新しいスライスに元のデータを全てコピーします。これにより、元のスライスと新しいスライスは独立します。

その他のテクニック

  • スライスのゼロ値: nilスライスは長さ 0 かつ容量 0 であり、要素を持たないが、append可能です。 以下に、2 次元配列の基本操作に関するチートシートを作成しました。これを先ほどの「Slice」と同じ階層に配置してください。

2 次元配列

2 次元配列は、配列の配列です。各要素はさらに配列となっており、行列のような構造を持ちます。Go では、2 次元配列のサイズは固定されており、スライスとは異なり動的なサイズ変更はできません。

基本的な 2 次元配列の操作

1. 2 次元配列の作成

// サイズが決まっている2次元配列を作成
var arr [3][4]int  // 3x4の2次元配列を作成(すべての要素は0)

// 初期値を設定して2次元配列を作成
arr := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

2. 2 次元配列の要素にアクセス

// 要素の取得と更新
arr := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

value := arr[1][2]  // 6を取得
arr[0][1] = 10      // arr[0][1]に10を設定

3. 2 次元配列のループ処理

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

// すべての要素にアクセスするための二重ループ
for i := 0; i < len(arr); i++ {
    for j := 0; j < len(arr[i]); j++ {
        fmt.Println(arr[i][j])
    }
}

// rangeを使ったループ
for i, row := range arr {
    for j, val := range row {
        fmt.Printf("arr[%d][%d] = %d\n", i, j, val)
    }
}

4. 2 次元配列の長さを取得

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

rows := len(arr)       // 行の数(2)
cols := len(arr[0])    // 列の数(3)

5. 2 次元配列のコピー

// 配列は固定サイズであるため、完全にコピーするには手動でループする必要があります
src := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

var dst [2][3]int
for i := range src {
    for j := range src[i] {
        dst[i][j] = src[i][j]
    }
}

以下に、Golang で 2 次元スライスに値を追加するためのチートシートを作成しました。コメントで簡単な説明を加えています。

2 次元スライス

2 次元スライスは、スライスのスライスとして実装されます。各スライスは異なる長さを持つことができ、動的に要素を追加することが可能です。

2 次元スライスの基本操作

1. 2 次元スライスの作成

// 空の2次元スライスを作成
matrix := [][]int{}

// 直接値を設定して作成
matrix := [][]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

2. 2 次元スライスに行を追加

appendはスライスの末尾に要素を追加し、容量が足りない場合は新しい配列を確保して返します。そのため、結果を元のスライスに再代入する必要があります。

// 新しい行を追加
newRow := []int{10, 11, 12}
matrix = append(matrix, newRow)
// matrixは[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]となる

3. 2 次元スライスの特定の行に要素を追加

// 1行目(インデックス0)に新しい要素を追加
matrix[0] = append(matrix[0], 4)
// matrixは[[1, 2, 3, 4], [4, 5, 6], [7, 8, 9]]となる

4. 2 次元スライスの特定の場所に要素を挿入

// 1行目(インデックス0)の2番目の位置(インデックス1)に値を挿入
index := 1
matrix[0] = append(matrix[0][:index+1], matrix[0][index:]...)
matrix[0][index] = 99
// matrixは[[1, 99, 2, 3, 4], [4, 5, 6], [7, 8, 9]]となる

5. 2 次元スライスの行を削除

// 2行目(インデックス1)を削除
matrix = append(matrix[:1], matrix[2:]...)
// matrixは[[1, 99, 2, 3, 4], [7, 8, 9]]となる

6. 2 次元スライスの特定の行から要素を削除

// 1行目(インデックス0)の3番目の要素(インデックス2)を削除
matrix[0] = append(matrix[0][:2], matrix[0][3:]...)
// matrixは[[1, 99, 3, 4], [7, 8, 9]]となる

補足

  • スライスは可変長であり、appendを使って動的に要素を追加できます。
  • 2 次元スライスはスライスのスライスであり、各行が異なる長さを持つことが可能です。
  • 挿入や削除の操作では、新しいスライスを作成して既存のスライスを再構築することが多いです。

2 次元スライス

2 次元スライスは、スライスのスライスとして実装されます。各スライスは異なる長さを持つことができ、動的に要素を追加することが可能です。

2 次元スライスの基本操作

1. 2 次元スライスの作成

// 空の2次元スライスを作成
matrix := [][]int{}

// 直接値を設定して作成
matrix := [][]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

2. 2 次元スライスに行を追加

// 新しい行を追加
newRow := []int{10, 11, 12}
matrix = append(matrix, newRow)
// matrixは[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]となる

3. 2 次元スライスの特定の行に要素を追加

// 1行目(インデックス0)に新しい要素を追加
matrix[0] = append(matrix[0], 4)
// matrixは[[1, 2, 3, 4], [4, 5, 6], [7, 8, 9]]となる

4. 2 次元スライスの特定の場所に要素を挿入

matrix := [][]int{
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9},
}

// 挿入したい位置のインデックス
index := 1

// 挿入する値
newValue := 99

// 挿入位置の前の部分をコピー
before := matrix[0][:index]  // beforeは[1]

// 挿入位置の後の部分をコピー
after := matrix[0][index:]   // afterは[2, 3]

// 新しいスライスを作成し、前の部分、新しい値、後の部分を結合
newRow := append(before, newValue)  // newRowは[1, 99]
newRow = append(newRow, after...)   // newRowは[1, 99, 2, 3]

// 元のスライスに新しい行を設定
matrix[0] = newRow

// 結果: matrixは[[1, 99, 2, 3], [4, 5, 6], [7, 8, 9]]となる

👆  同じ  👇

// 1行目(インデックス0)の2番目の位置(インデックス1)に値を挿入
index := 1
matrix[0] = append(matrix[0][:index+1], matrix[0][index:]...)
matrix[0][index] = 99
// matrixは[[1, 99, 2, 3, 4], [4, 5, 6], [7, 8, 9]]となる

5. 2 次元スライスの行を削除

// 2行目(インデックス1)を削除
matrix = append(matrix[:1], matrix[2:]...)
// matrixは[[1, 99, 2, 3, 4], [7, 8, 9]]となる

6. 2 次元スライスの特定の行から要素を削除

// 1行目(インデックス0)の3番目の要素(インデックス2)を削除
matrix[0] = append(matrix[0][:2], matrix[0][3:]...)
// matrixは[[1, 99, 3, 4], [7, 8, 9]]となる

補足

  • スライスは可変長であり、appendを使って動的に要素を追加できます。
  • 2 次元スライスはスライスのスライスであり、各行が異なる長さを持つことが可能です。
  • 挿入や削除の操作では、新しいスライスを作成して既存のスライスを再構築することが多いです。

このチートシートを参考にして、Golang の 2 次元スライスの操作を習得してください。

Map

マップの宣言と初期化

//----------------------------------------
// Map declaration and initialization
//----------------------------------------

// マップを宣言(初期化なし)
var m map[string]int

// マップを初期化
m = make(map[string]int)

// 宣言と初期化を同時に行う
m := make(map[string]int)

// リテラルで初期化
m := map[string]int{
    "apple":  120,
    "banana": 150,
}

値の設定と取得

//----------------------------------------
// Set and get values in a map
//----------------------------------------

// 値を設定
m["apple"] = 130

// 値を取得
price := m["apple"]
fmt.Println("Apple price:", price) // 出力: Apple price: 130

キーの存在確認

//----------------------------------------
// Check if a key exists in the map
//----------------------------------------

// キーの存在確認
price, exists := m["banana"]
if exists {
    fmt.Println("Banana price:", price)
} else {
    fmt.Println("Banana is not found")
}

値の削除

//----------------------------------------
// Delete a key-value pair from the map
//----------------------------------------

// 値の削除
delete(m, "apple")

マップの長さを取得

//----------------------------------------
// Get the number of key-value pairs in the map
//----------------------------------------

length := len(m)
fmt.Println("Number of items in the map:", length)

マップのイテレーション

//----------------------------------------
// Iterate over a map
//----------------------------------------

for fruit, price := range m {
    fmt.Printf("%s costs %d yen\n", fruit, price)
}

キーの一意性(重複不可)

//----------------------------------------
// Map keys must be unique
//----------------------------------------

// キーが重複すると、値が上書きされる
m["banana"] = 150
m["banana"] = 180 // 既存の"banana"の値が上書きされる

fmt.Println("Updated Banana price:", m["banana"]) // 出力: Updated Banana price: 180

ネストされたマップ

//----------------------------------------
// Nested maps
//----------------------------------------

productPrices := map[string]map[string]int{
    "fruits": {
        "apple":  100,
        "banana": 200,
    },
    "vegetables": {
        "carrot": 80,
        "onion":  50,
    },
}

// ネストされたマップの値の取得
applePrice := productPrices["fruits"]["apple"]
fmt.Println("Apple price from nested map:", applePrice) // 出力: Apple price from nested map: 100

マップのコピー(シャローコピー)

//----------------------------------------
// Shallow copy of a map
//----------------------------------------

newMap := make(map[string]int)
for k, v := range m {
    newMap[k] = v
}

// newMapはmのコピーだが、完全なディープコピーではない

マップと構造体の組み合わせ

//----------------------------------------
// Using maps with structs
//----------------------------------------

type Product struct {
    Name  string
    Price int
}

products := make(map[string]Product)
products["apple"] = Product{Name: "Apple", Price: 120}

// 構造体を使ったマップの値の取得
apple := products["apple"]
fmt.Println("Product:", apple.Name, "Price:", apple.Price) // 出力: Product: Apple Price: 120

マップの JSON 操作

//----------------------------------------
// Working with maps in JSON
//----------------------------------------

import (
    "encoding/json"
    "fmt"
)

// マップをJSONエンコード
jsonData, err := json.Marshal(m)
if err != nil {
    fmt.Println(err)
}
fmt.Println("JSON data:", string(jsonData)) // 出力: {"banana":180}

// JSONからマップをデコード
jsonStr := `{"apple":130,"banana":150}`
var decodedMap map[string]int
err = json.Unmarshal([]byte(jsonStr), &decodedMap)
if err != nil {
    fmt.Println(err)
}
fmt.Println("Decoded map:", decodedMap) // 出力: map[apple:130 banana:150]

注意点

  • マップのキーは一意でなければならない。同じキーを再度追加すると、そのキーの値が上書きされます。
  • マップは順序を保証しない。イテレーションの順序はランダムです。
  • マップのゼロ値はnilnilマップに対して要素を追加しようとするとパニックが発生しますので、必ず初期化する必要があります。

Sort

Slice

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

// 昇順ソート
sort.Slice(slice, func(i, j int) bool {
    return slice[i] < slice[j]
})
fmt.Println(slice) // [1 2 3 4 5 6]

// 降順ソート
sort.Slice(slice, func(i, j int) bool {
    return slice[i] > slice[j]
})
fmt.Println(slice) // [6 5 4 3 2 1]

シンプルに昇順ソートするには、sort.Intssort.Stringssort.Float64sを使う。

//----------------------------------------
// Sort slice
//----------------------------------------
nums := []int{42, -10, 0, 8}
sort.Ints(nums)
fmt.Println(nums) // [-10 0 8 42]

strs := []string{"banana", "apple", "cherry"}
sort.Strings(strs)
fmt.Println(strs) // ["apple", "banana", "cherry"]

floats := []float64{3.14, 1.59, 2.65}
sort.Float64s(floats)
fmt.Println(floats) // [1.59, 2.65, 3.14]

逆順、カスタムソート

sort.Reverse(slice)

nums := []int{42, -10, 0, 8}

// IntSliceを使ってソート
sort.Sort(sort.IntSlice(nums)) // 昇順にソートされる
fmt.Println(nums) // [-10 0 8 42]

// 逆順にソート
sort.Sort(sort.Reverse(sort.IntSlice(nums)))
fmt.Println(nums) // [42 8 0 -10]

sort.Slice

strings の逆順(反転)ソート

//----------------------------------------
// Reverse string
//----------------------------------------
func reverseString(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

str := "hello"
reversed := reverseString(str)
fmt.Println(reversed) // "olleh"

ファイル操作

go.mod が存在するプロジェクトルートディレクトリを取得する

func getRootDir() (string, error) {
	currentDir, err := os.Getwd()
	if err != nil {
		return "", err
	}

	for {
		if _, err := os.Stat(filepath.Join(currentDir, "go.mod")); err == nil {
			return currentDir, nil
		}

		parentDir := filepath.Dir(currentDir)
		if parentDir == currentDir {
			return "", fmt.Errorf("go.mod not found")
		}
		currentDir = parentDir
	}
}

Usage:

targetFile := filepath.Join(getRootDir(), "file.txt")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment