go言語アルゴリズムやTipsなどメモ
内容
go言語で自分がよく使う書き方やTipsのまとめ
並行処理関係は以下
BenchmarkTestは以下
Tips
入力値
入力値を受け取る
文字列で受け取る
var s string fmt.Scan(&s)
整数で受け取る
var N int fmt.Scan(&N)
複数の入力をまとめて受け取ることもできる
var a, b, c int fmt.Scan(&a, &b, &c)
空白区切りの入力値を受け取ってスライスにする
N文字の空白区切りの入力された数値をスライスにする
- fmt.Scanは、改行もしくは空白を区切ってしまうので、N回ループする
l := make([]int, N) for i := range l { fmt.Scan(&l[i]) }
20 24 59 ← 入力値 [20 24 59]
空白区切りの入力値を一行受け取ってスライスにする
参考: Goで標準入力から文字列や数値列を取得する - Qiita
package main import ( "bufio" "fmt" "os" ) func main() { s := bufio.NewScanner(os.Stdin) s.Scan() input := s.Text() l := strings.Split(input, " ") fmt.Println(l) }
3 42 25 ← 入力 [3 42 25] ← 全部stringなので注意
全部intのスライスにしたい場合はこんな感じかな
- この方法よりは上記のfmt.Scanを使ったほうがよっぼど簡単だと思う
package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func main() { s := bufio.NewScanner(os.Stdin) s.Scan() input := s.Text() l := strings.Split(input, " ") toIntL := func(l []string) []int { var intL []int for _, v := range l { i, _ := strconv.Atoi(v) intL = append(intL, i) } return intL } fmt.Println(toIntL(l)) }
実行結果
431 12 64 ←入力値 [431 12 64]
整数の数列をソート
package main import ( "fmt" "sort" ) func main() { a := []int{10, 3, 4, 1} A := a sort.Ints(A) fmt.Println(A) B := a fmt.Println(sort.IntSlice(B)) // 上と同じ出力 C := a sort.Sort(sort.IntSlice(C)) fmt.Println(C) // 上と同じ出力 D := a sort.Sort(sort.Reverse(sort.IntSlice(D))) fmt.Println(D) // 逆順に出力 }
結果 [1 3 4 10] [1 3 4 10] [1 3 4 10] [10 4 3 1]
文字列を辞書順にソート
いったんスライスにしてソートしてからjoinで戻す方法
参考
package main import ( "fmt" "sort" "strings" ) func main() { var s string fmt.Scan(&s) sl := strings.Split(s, "") sort.Strings(sl) sj := strings.Join(sl, "") fmt.Println(sj) }
実行
gasdbfgaa ← 入力 aaabdfggs ← 出力
逆順にソートしたいときは以下を使う
- sort.Sort(sort.Reverse(sort.StringSlice(s)))
関数化したもの
複数条件で優先順位をつけたソート
安定ソートを数回するといい - 安定ソート(SliceStable)を使うことで、最初の並びをなるべくそのままにしつつ並べ替える
参考 - GoのSliceをSortする(sort.Sliceとsort.SliceStable) - Qiita
package main import ( "fmt" "sort" ) type Person struct { ID int Name string } func main() { group := []Person{ {ID: 2, Name: "A"}, {ID: 1, Name: "A"}, {ID: 3, Name: "A"}, {ID: 2, Name: "C"}, {ID: 1, Name: "B"}, {ID: 1, Name: "C"}, {ID: 3, Name: "C"}, {ID: 3, Name: "B"}, {ID: 2, Name: "B"}, } // groupをgroup1, group2に代入してそれぞれの変化を見る group1 := group // 最初にID順にソート sort.SliceStable(group1, func(i, j int) bool { return group1[i].ID < group1[j].ID }) // 次にName順にソート。この時、Nameが同じであれば上でソートされたID順の並びを崩さない(安定ソート) sort.SliceStable(group1, func(i, j int) bool { return group1[i].Name < group1[j].Name }) fmt.Printf("IDで安定SortしてからNameで安定ソート:%+v\n", group1) group2 := group // 最初にName順にソート sort.SliceStable(group2, func(i, j int) bool { return group2[i].Name < group2[j].Name }) // 次にID順にソート。この時、IDが同じであれば上でソートされたName順の並びを崩さない(安定ソート) sort.SliceStable(group2, func(i, j int) bool { return group2[i].ID < group2[j].ID }) fmt.Printf("Nameで安定SortしてからIDで安定ソート:%+v\n", group2) }
実行結果
IDで安定SortしてからNameで安定ソート:[{ID:1 Name:A} {ID:2 Name:A} {ID:3 Name:A} {ID:1 Name:B} {ID:2 Name:B} {ID:3 Name:B} {ID:1 Name:C} {ID:2 Name:C} {ID:3 Name:C}] Nameで安定SortしてからIDで安定ソート:[{ID:1 Name:A} {ID:1 Name:B} {ID:1 Name:C} {ID:2 Name:A} {ID:2 Name:B} {ID:2 Name:C} {ID:3 Name:A} {ID:3 Name:B} {ID:3 Name:C}]
数字の各桁の数値をすべて足す
方法1.
sum := 0 for n > 0 { sum += n % 10 n /= 10 }
⇒ n が 12345なら sは15
方法2.
sum := 0 for i := n; i > 0; i /= 10 { sum += i % 10 }
ポインタ
変数のポインタ
package main import ( "fmt" ) func main() { t := 10 fmt.Println(t) change := func(t *int) { *t = 11 } change(&t) fmt.Println(t) }
実行結果
10 11
グリッド
二次元配列を組み立て
package main import ( "fmt" "strings" ) func main() { var h, w int fmt.Scan(&h, &w) // high, width var p [][]string for i := 0; i < h; i++ { var l string fmt.Scan(&l) p = append(p, strings.Split(l, "")) } fmt.Println(p) fmt.Println(p[0][1]) }
実行結果
3 5 12345 67891 23456 [[1 2 3 4 5] [6 7 8 9 1] [2 3 4 5 6]] ← 出力 2 ← 出力
行列計算
n * m
の二次元配列を入力から受け取り
var n, m int fmt.Scan(&n) fmt.Scan(&m) a := make([][]int, n) for i := 0; i < n; i++ { a[i] = make([]int, m) for j := 0; j < m; j++ { fmt.Scan(&a[i][j]) } }
n * mの行列Aと m * l の行列Bの積としてn * lの行列Cを作成
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_7_D
var n, m, l int fmt.Scan(&n) fmt.Scan(&m) fmt.Scan(&l) a := make([][]int, n) for i := 0; i < n; i++ { a[i] = make([]int, m) for j := 0; j < m; j++ { fmt.Scan(&a[i][j]) } } b := make([][]int, m) for i := 0; i < m; i++ { b[i] = make([]int, l) for j := 0; j < l; j++ { fmt.Scan(&b[i][j]) } } c := make([][]int, n) for i := 0; i < n; i++ { c[i] = make([]int, l) } for k := 0; k < n; k++ { for j := 0; j < l; j++ { for i := 0; i < m; i++ { c[k][j] += a[k][i] * b[i][j] } } } fmt.Println(c)
構造体とメソッド
構造体
例
こんな感じにstructを宣言して初期化できる
package main import "fmt" type Person struct { name string age int weight float64 } func main() { person1 := Person{name: "Taro", age: 21, weight: 60.12} // フィールド名を指定するとわかりやすい fmt.Println(person1) person2 := Person{"Akira", 22, 61.12} // 順番通りなら指定しなくてもいい fmt.Println(person2) }
実行結果
{Taro 21 60.12} {Akira 22 61.12}
メソッド
goにはクラスはないが、構造体にメソッドを追加することでクラスメソッドのように使える
例
上で定義したPerson構造体の要素をすべてstringにしたスライスを返すメソッドを追加した例
func (p *Person) toString() []string { var str []string str = append(str, fmt.Sprintf("%s", p.name)) str = append(str, fmt.Sprintf("%d", p.age)) str = append(str, fmt.Sprintf("%f", p.weight)) return str } func main() { person1 := Person{name: "Taro", age: 21, weight: 60.12} fmt.Println(person1.toString()) }
実行結果
[Taro 21 60.120000]
例2
Person構造体のスライスPersonsを定義して、要素をすべてinterfaceの二次元配列にして返す関数を使ってみた例
type Person struct { name string age int weight float64 } type Persons []Person func (p *Person) toString() []string { var str []string str = append(str, fmt.Sprintf("%s", p.name)) str = append(str, fmt.Sprintf("%d", p.age)) str = append(str, fmt.Sprintf("%f", p.weight)) return str } func (p *Person) toInterfaceSlice() []interface{} { var is []interface{} is = append(is, p.name) is = append(is, p.age) is = append(is, p.weight) return is } func (ps *Persons) toInterfaceSlices() [][]interface{} { var iss [][]interface{} for _, p := range *ps { iss = append(iss, p.toInterfaceSlice()) } return iss } func main() { person1 := Person{name: "Taro", age: 21, weight: 60.12} fmt.Printf("要素が全部stringのスライスに変換 %v\n", person1.toString()) fmt.Printf("要素が全部interfaceのスライスに変換 %v\n", person1.toInterfaceSlice()) persons := Persons{} persons = append(persons, person1) person2 := Person{name: "Akira", age: 22, weight: 61.12} persons = append(persons, person2) fmt.Printf("要素が全部interfaceの二次元スライスに変換 %v\n", persons.toInterfaceSlices()) }
実行結果
要素が全部stringのスライスに変換 [Taro 21 60.120000] 要素が全部interfaceのスライスに変換 [Taro 21 60.12] 要素が全部interfaceの二次元スライスに変換 [[Taro 21 60.12] [Akira 22 61.12]]
コード https://play.golang.org/p/FeJx-_wXv68
Enum
参考
4 iota enum examples · YourBasic Go
エラーハンドリング
参考
【go】golangのエラー処理メモ - ②. 例外はないがエラーハンドリングはできるよ(インスタンスや型でハンドリング) - tweeeetyのぶろぐ的めも
エラー・ハンドリングについて(追記あり) — プログラミング言語 Go | text.Baldanders.info
Golangのエラー処理とpkg/errors | SOTA
Errors are values - The Go Blog ← 同じ関数を複数回呼ぶ時のエラーをまとめて扱う
reflect
こちらにまとめた ludwig125.hatenablog.com
アルゴリズム
全N個のものをM個単位で処理する
以下のようなlistを指定の件数ずつ分割して処理したいとき
package main import ( "fmt" ) func main() { l := []int{1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10} for _, i := range l { fmt.Println(i) } } // 結果 /* 1 2 3 4 5 6 6 7 8 9 10 */
以下のようにしてみた
package main import ( "fmt" ) func main() { l := []int{1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10} chunkSize := 3 for start := 0; start < len(l); start += chunkSize { end := start + chunkSize if end > len(l) { end = len(l) } fmt.Println(l[start:end]) fmt.Println("---") } } // 結果 /* [1 2 3] --- [4 5 6] --- [6 7 8] --- [9 10] --- */