- 投稿日:2020-11-28T23:33:43+09:00
AtCoder Regular Contest 109のメモ
前置き
Atcoderをやってみたので、自分用のメモです。
あとから加筆・修正する予定です。問題
https://atcoder.jp/contests/arc109
A
Q_A.gopackage main import ( "fmt" ) func main() { var a,b,x,y int fmt.Scanf("%d %d %d %d", &a, &b, &x, &y) var ans int if a == b { ans = x } else { if a > b{ ans = (a - (b+1)) * y + x if ans > (2 * (a-b) -1) * x { ans = (2 * (a-b) -1) * x } } else { ans = (b - a) * y + x if ans > ((1 + 2 * (b-a)) * x) { ans = ((1 + 2 * (b-a)) * x) } } } fmt.Printf("%d\n", ans) }B
Q_B.gopackage main import ( "fmt" ) func main() { var n int64 fmt.Scanf("%d", &n) var X int64 = n+1 var dis int64 =0 var i int64 for i=1; ; i++{ X = X - i if X < 0{ break }else{ dis +=1 } } ans := n + 1 - dis fmt.Printf("%d\n", ans) }C
Q_C.gopackage main import ( "fmt" "strings" ) func main() { var n, k int64 fmt.Scanf("%d %d", &n, &k) var s string fmt.Scanf("%s", &s) S := strings.Split(s, "") ir := make([][]string, k) var i, j int64 for i=0; i < k; i++ { ir[i] = make([]string, n) } var index int64 = 1 var right int64 for j=0; j<n; j++{ right = (j + index) % n ir[0][j] = judge(S[j], S[right]) } for i=1; i<k; i++{ index = (index + index) % n for j=0; j<n; j++{ right = (j+index)%n ir[i][j] = judge(ir[i-1][j], ir[i-1][right]) } } fmt.Printf("%s", ir[k-1][0]) } func judge(left string, right string) string { if left == right { return left } else if (left =="R") && (right == "P") { return "P" } else if (left =="R") && (right == "S") { return "R" } else if (left =="S") && (right == "R") { return "R" } else if (left =="S") && (right == "P") { return "S" } else if (left =="P") && (right == "R") { return "P" } else if (left =="P") && (right == "S") { return "S" } return "S" }D
覚えてたら後で書きます。
E
覚えてたら後で書きます。
F
覚えてたら後で書きます。
- 投稿日:2020-11-28T18:41:11+09:00
goの超初心者向けのチュートリアル
近々go言語に触れるかもしれないので、無知の状態からやってみたことの備忘録です。
前提
・Mac
・とりあえず手っ取り早く動かせる状態にしたい
・go触ったことないインストール
公式ページからパッケージをインストールするだけ。
https://golang.org/dl/Homebrewなどでインストールする方法もあるようですがcliに苦手意識あるので簡単なやり方で。。。
開発ツール
とりあえずVisualStudioCode
(vscode最強だと思ってる)以下の拡張機能を追加。
・Go(https://marketplace.visualstudio.com/items?itemName=golang.Go)
※Go Nightlyという拡張機能は入れないほうがいい(goのプレビュー版で併用して動かせないため)とりあえずHelloWorld
公式ページのチュートリアルをやってみる
適当な.goファイルを作って以下の内容を記載
main.gopackage main import "fmt" func main() { fmt.Println("Hello,World!") }ターミナルでファイル実行するとHelloWorld表示される
go run main.go Hello,World!パッケージの追加
node.jsでいうnode_module的なもの?
main.gopackage main import "fmt" import "rsc.io/quote" //追加 func main() { fmt.Println("Hello,World!") fmt.Println(quote.Go()) //追加 }ターミナルで以下のコマンドを実行するとgo.modファイル(パッケージ管理ファイル?)とgo.sumファイル(インストール済みのパッケージを記載?)が作られてquoteパッケージの読み込み設定が記載される
(node.jsでいうpackage.jsonですね)go mod init maingo.modmodule main go 1.15 require rsc.io/quote v1.5.2再度runコマンドを実行すると追加したquoteがインストールされたっぽい。
npm install を自動で実施してくれた感じですね。ひとまず初めの一歩を踏み出せるところまでです。
公式チュートリアルはまだ続きがあるので、後日追記する。その他やりたいこと
・自動ビルド?のパッケージがあるっぽいのでそいつを導入してみる
・web画面表示したい
- 投稿日:2020-11-28T18:05:05+09:00
初学者向け Windows でGoの環境構築をした話
はじめに
普段は製造業の情報システム部で社内SEとしてシステムの保守運用をしている社会人1年目の新人SEです。
普段はJavaをメインで使っているのですが、モダンな技術を知りたく手始めにGo言語の勉強をしようと思います。
環境構築するついでに、これからGo言語で初めてプログラミング言語に触れる初学者の方の助力になればと思いメモ書きとして残しておきます。環境変数何それ???ってレベルのガチガチの初学者方向けの記事になってしまいましたが丁寧なのに越したことはないはず。。。
あ、パスとかディレクトリは分かっている設定で書いてますので分からない人はググってね。Goの環境構築
1.インストーラのダウンロード
下記リンクでダウンロードできます。
https://golang.org/dl/今回はWindows版をダウンロードします。
ダウンロードができたらインストーラを開いてインストールへ!
2.インストール
基本的に特に何もいじらず指示通りインストールすれば問題ないです。
インストール先は各人のお好みで。
特にこだわりがなければC:\ or D:\ の直下に配置してあげましょう。インストールができたら環境変数の設定へ!
3.環境変数の設定
コントロールパネルを開く
システムとセキュリティ → システム → システムの詳細設定 → 環境変数
システム環境変数(S) にある Path を選択して 編集(I) を押下
自動で登録されていた場合はそのままOKを押して閉じて大丈夫!
自動で登録されていなかった場合
環境変数の設定が終わったら最後の確認作業へ!
4.確認
コマンドプロンプトを開いて
go versionと入力しインストールしたものと同じversionが出力されていれば環境変数の設定はできています。
ここで失敗する場合、環境変数の設定が間違っている可能性が高いのでもう一度環境変数の設定を見直しましょう!
*インストールの際にパスを任意に設定している場合、binのフォルダまで移動して実行すると成功するかも
下記コマンドでbinのディレクトリに移動できます。
("binがあるディレクトリのパス" に実際のパスを入力してください。)cd "binがあるディレクトリのパス"では、試しに動くか確かめてみましょう。
"Hellow Go" でも出力してみましょうか
test.go という名前のファイルを作成して、下記内容を記載package main import "fmt" func main() { fmt.Printf("Hello Go\n") }test.goのあるディレクトリで下記コマンドプロンプトを実行する
go run test.go無事 Hellow Go が出力されたらOK!
最後に
ガチガチの初学者向けの記事になってしまいましたが、私がプログラミング始めたばかりのころを思い出しながら書いてみました。
分かりにくかったらすいません!
ではみなさん、素敵なGolang lifeを!
- 投稿日:2020-11-28T13:58:54+09:00
Goで始める分散データ処理。Bigsliceパッケージ入門
はじめに
Go言語で開発された有名な製品はDockerやKubernetesを筆頭に数多く存在します。アプリケーション開発としてもWebAPIのバックエンドやCLIツール開発で利用されることも増えていると感じます。IoTの文脈ではTinyGoなど組み込みプログラム領域でも進化を続けていて、WebAssembly(WASM)向けビルドと相まって今後さらなる拡張に期待を持っている人も多いかと思います。
一方で、大規模(1台のサーバに収まらない)データの分散処理分野では、Apache Spark(もちろんHadoop, YARN, etc.)とそのエコシステムが圧倒的に強いと感じます。AWS上であればSparkのマネージドサービスたるAWS Glueがありますし(EMRもありますが)、GCPだとDataprocでSpark(DataflowをApache Beamで扱うことが多そうですが)が広く使われていると思います。
Apache SparkはGoバインドのSDKはありますがαステータス1、Apache BeamもPySparkのようなGo SDK がありますが、experimental2でプロダクション利用は非推奨とのこと。そのため、Goで大規模データに対する分散処理を含んだアプリケーションのロジックを実装するのは既存のフレームワークでは厳しい状態だたと思います。私自身、少なくても日本でこの領域にGoで処理しているという話は聞いたことがないです3。この領域でのGoの存在感は意外なほど小さいです。
Bigslice
The Bigslice gopher design was inspired by Renee French.
Bigslice is a system for fast, large-scale, serverless data processing using Go.
(Bigsliceは、Goを利用した高速かつ大規模データ処理のためのサーバーレスな仕組みです。)■Bigslice(公式)
https://bigslice.io/そんな分散処理界隈ですが、Bigsliceというデータ処理フレームワークが存在します。サーバレスを謳っています。公式ドキュメントにもApache SparkやFlume Javaとの違いを触れていますが、カテゴリーとしては同様と扱って良いと思います。
Map, Filter, Reduce, Joinなどデータのコレクション操作のようなAPIを提供し、Bigslice側でクラウド上にアドホックなクラスタを作成し、透過的に分散処理を行ってくれるとのことです(スゴイ)。通常のGoパッケージと同じくBigsliceパッケージをimportして既存のGoコードと同じ用にコンパイルができるとのこと。これだけだとその凄さが分かるようであまり分からないので、早速使ってみましょう。
サンプルを実行(ローカル端末で実行)
まずは公式ドキュメントのGetting startedにかかれているサンプルコードです。この界隈だとよくある Word Count です。
wordcount.gopackage main import ( // 省略 "github.com/grailbio/bigslice" // 省略 ) var wordCount = bigslice.Func(func(url string) bigslice.Slice { slice := bigslice.ScanReader(8, func() (io.ReadCloser, error) { // 入力読み込み。8はシャード数(分散数) resp, err := http.Get(url) if err != nil { return nil, err } if resp.StatusCode != 200 { return nil, fmt.Errorf("get %v: %v", url, resp.Status) } return resp.Body, nil }) slice = bigslice.Flatmap(slice, strings.Fields) // 入れ子をフラットに変換 slice = bigslice.Map(slice, func(token string) (string, int) { // 1単語ごとに数値1と表現(変換) return token, 1 }) slice = bigslice.Reduce(slice, func(a, e int) int { // 集計処理 return a + e }) return slice })bigsliceパッケージを利用していることが分かると思いますが、ここに分散コレクション操作を実装していきます。最初の
func(url string) bigslice.Slice
に渡される 引数urlはmain関数から渡されます。その内部の実装としては以下のようなイメージです。
- bigslice.ScanReaderで入力の読み込み。ここではURLのテキストを読み込み
- bigslice.FlatmapでSliceと呼ばれるbigsliceの分散データ表現に変換。ここでは文字列に変換
- bigslice.Mapで射影(変換処理)。ここでは入力トークンを、キーと数値1に変換
- bigslice.Reduceで集計。先程のトークンが同じ場合にReduceが呼ばれ、個々では単語ごとにカウントを合算
この
wordCount
をmain関数から呼び出します。シェークスピアのテキストファイル(5.2MBほど)をワードカウントしてみます。wordcount.gopackage main import ( // 省略 "github.com/grailbio/bigslice/sliceconfig" ) const shakespeare = "https://ocw.mit.edu/ans7870/6/6.006/s08/lecturenotes/files/t8.shakespeare.txt" type counted struct { token string count int } func main() { sess := sliceconfig.Parse() defer sess.Shutdown() ctx := context.Background() // 処理の開始 tokens, err := sess.Run(ctx, wordCount, shakespeare) if err != nil { log.Fatal(err) } // 結果読み込み scanner := tokens.Scanner() defer scanner.Close() var ( token string count int counts []counted ) for scanner.Scan(ctx, &token, &count) { counts = append(counts, counted{token, count}) } if err := scanner.Err(); err != nil { log.Fatal(err) } // 出現数で降順ソート sort.Slice(counts, func(i, j int) bool { return counts[i].count > counts[j].count }) // 上位10件を表示 for _, count := range counts { fmt.Println(count.token, count.count) } }実行してみます。
実行結果$ go run main.go 2020/11/28 14:32:34 http: serve :3333 the 23242 I 19540 and 18297 to 15623 of 15544 a 12532 my 10824 in 9576 you 9081 is 7851無事ワードカウントができたようです。the, I, and, to, ofなどが出現数が多いようですね。
Windows環境だと下記エラーが出てしまい実行できないようなので、試すときはWSLなどで実行してみてください。
Windowsで動かした場合> go run main.go # github.com/grailbio/base/status ..\..\..\..\pkg\mod\github.com\grailbio\base@v0.0.9\status\stream.go:112:23: undefined: syscall.SIGWINCH ..\..\..\..\pkg\mod\github.com\grailbio\base@v0.0.9\status\term.go:120:8: undefined: syscall.Termios ..\..\..\..\pkg\mod\github.com\grailbio\base@v0.0.9\status\term.go:121:31: not enough arguments in call to syscall.Syscall6 略AWS環境で実行
透過的にといういうくらいなので、AWSのスポットインスタンスで上記のワードカウントを動かすことができるようです。なんとなくHadoopのMapReduceを思い出します(YARNコンテナではなく、EC2が起動するのはなかなか強いなと思いますね)
仕組みとしては、grailbio/bigmachineを利用しています。こちでEC2を起動しssh経由でEC2にバイナリを送りつける仕組みなようです。
こうした背景から、事前に公開鍵で認証済みのAMIを作成したり、ssh(22)やHTTPS(443)のインバウンドを許可したセキュリティグループを作成しておいたり、ローカル端末で動かす場合はEC2インスタンスの起動権限を付与したIAMユーザを払い出す必要があります。
ちなみに、AWS上にSecurityGroup名はデフォルトで
bigslice
です。諸々準備を終えて以下のコマンドで動くとドキュメントに書いています。別記事でこのあたりの設定方法はまとめていきたいと思います。ざっくり書くと、
①セキュリティグループ+EC2 AMIを作成(ここが長い)
②コマンドインストール($GOPATH/binをPATHに追加していない場合は忘れないように)bigsliceコマンドのインストールGO111MODULE=on go get github.com/grailbio/bigslice/cmd/bigslice@latest③AWSキーを環境変数に載せる。bigsliceはProfileには非対応なようです
キーは適時読み替えください$ export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE $ export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY $ export AWS_REGION=ap-northeast-1④セットアップコマンドを実行します。
$ bigslice setup-ec2 bigslice: found existing bigslice security group sg-0f061b686f38ab36b bigslice: set up new security group sg-0f061b686f38ab36b bigslice: wrote configuration to /home/mano/.bigslice/config⑤
~/.bigslice/conifg
のAMI-IDを書き換え$ head /home/mano/.bigslice/config instance aws aws/env param aws/env region = "ap-northeast-1" // the default AWS region for the session param bigmachine/ec2system ( // 省略 ami = "ami-0f5253b670d69a556" // ★書き換えます // 省略⑥bigsliceコマンドの実行
GO111MODULE=on bigslice run main.go 2020/11/30 23:10:18 http: serve :3333 2020/11/30 23:10:18 slicemachine: 0 machines (0 procs); 1 machines pending (3 procs) 2020/11/30 23:10:18 slicemachine: 0 machines (0 procs); 2 machines pending (6 procs) 2020/11/30 23:10:18 slicemachine: 0 machines (0 procs); 3 machines pending (9 procs) ...そうすると、下記のようにEC2がデフォルトで3台起動します。端末のbigsliceを殺すとEC2インスタンスが起動しっぱなしになるためご注意ください。
内部詳細
少しですが公式ドキュメントに記載がありました。
- Bigsliceのデータ表現について
- 並列処理(内部でDAGに変換され、CPU1コアを1Procリソースとして分割する記述
まとめ
EC2をオンデマンドに起動し、分散処理を行うBigsliceパッケージを紹介しました。
GCP、Azureなどには対応していなかったり、AWSでもEC2のみの対応のようですが非常に心くすぐられるパッケージです。
ドキュメントなどがまだ出揃っておらず、試行錯誤が必要ですが上手くハマれば非常に面白いプロダクトであると思います。
今後大規模データに対する分散処理の領域もGo言語でアプリケーションを書く機会が増えていけば良いなと思います。
2020/11/28現在、READMEを読む限り「2016-07-27: First very alpha version.」と書かれていたことから判断 ↩
2020/11/28現在、「The Go SDK is currently experimental, does not yet offer any compatibility guarantees and is not recommended for production usage. It supports most features, but not all.」とドキュメントに記載 ↩
少なくても日本では?。Apache KafkaやAWS Kinesis DataStream などメッセージングサービスを使った逐次処理で比重が傾いて、そもそもこういった需要が減ったからが理由な気がしないでもないです。 ↩
- 投稿日:2020-11-28T11:52:59+09:00
LeetCodeに毎日挑戦してみた 35. Search Insert Position(Python、Go)
はじめに
無料英単語サイトE-tanを運営中の@ishishowです。
プログラマとしての能力を上げるために毎日leetcodeに取り組み、自分なりの解き方を挙げていきたいと思います。
Leetcodeとは
leetcode.com
ソフトウェア開発職のコーディング面接の練習といえばこれらしいです。
合計1500問以上のコーデイング問題が投稿されていて、実際の面接でも同じ問題が出されることは多いらしいとのことです。golang入門+アルゴリズム脳の強化のためにgoとPythonで解いていこうと思います。(Pythonは弱弱だが経験あり)
10問目(問題35)
35. Search Insert Position
- 問題内容
Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
(日本語訳)
個別の整数のソートされた配列とターゲット値が与えられた場合、ターゲットが見つかった場合はインデックスを返します。そうでない場合は、順番に挿入された場合のインデックスを返します。
**
Example 1:
Input: nums = [1,3,5,6], target = 5 Output: 2Example 2:
Input: nums = [1,3,5,6], target = 2 Output: 1Example 3:
Input: nums = [1,3,5,6], target = 7 Output: 4Example 4:
Input: nums = [1,3,5,6], target = 0 Output: 0Example 5:
Input: nums = [1], target = 0 Output: 0考え方
バイナリーサーチを使って実装しました
midとターゲットを比較して二分探索していきます
範囲を絞っていき、見つかったらreturnします
- 解答コード
def searchInsert(self, nums, target): # works even if there are duplicates. l , r = 0, len(nums)-1 while l <= r: mid=(l+r)/2 if nums[mid] < target: l = mid+1 else: if nums[mid]== target and nums[mid-1]!=target: return mid else: r = mid-1 return l
- Goでも書いてみます!
func searchInsert(nums []int, target int) int { l := 0 r := len(nums) - 1 for (r >= l) { mid := l + (r - l) / 2 if (nums[mid] == target) { return mid } else if (nums[mid] > target) { r = mid - 1; } else { l = mid + 1; } } return l }
- 投稿日:2020-11-28T02:51:54+09:00
[逐次更新]Goのドキュメントを読んでく
背景
同僚達とGoのドキュメントを読んでこうって取り組みを開始しました。
毎週決められた日時で担当者が発表…とはいえ私の理解・予習・復習の為にも、雑に和約っぽいもの(意訳)と個人の感想を書き連ねていきます。
内容の正しさは保証しないので、誤り見つけましたらご指摘ください。
ちょいちょい更新する予定です。今回読むもの
https://github.com/golang/go/wiki/CodeReviewComments
本題
【Gofmt】
gofmt
をコード上で実行すると、自動的に多くの機械的システムの問題を修正します。
ほとんど全てのGoコードがgofmt
を使用しております。
このドキュメントの残りの部分は機械的でないスタイルを対象としおります。
gofmt
の上位互換であるgoimports
を代わりに使用することもできます。
goimports
は必要に応じてimport行を追加・削除をすることができます。つまり
VSCodeなりなんだりのGoをサポートしているエディタで
gofmt
、goimports
のモジュールを使用すれば、自動で修正・importしてくれるよってお話。便利なので忘れずにやらねば。【Comment Sentences】
参考)https://golang.org/doc/effective_go.html#commentary.
(関数・定数の)宣言時のコメント文は、多少冗長に感じたとしても、完全な文章でなければいけません。
このアプローチによってgodocドキュメントに展開したときのフォーマットが良くなります。
コメントは宣言されているものの名前で始まり、ピリオドで終わる必要があります。つまり
宣言に対するコメントは、名称で始めて文章にする。(ピリオドで終わらせる)
godoc -http=:8080
でgodocドキュメントを確認してみるか…【Contexts】
context.Context
型の値には、APIとプロセスの境界を跨いで、セキュリティ証明書・追跡情報・期限・キャンセルシグナルが含まれております。
Goプログラムは明示的に、RPCやHTTPリクエストからリクエストの発信まで、コールチェイン全体に沿ってContexts
を渡します。
Context
を使用するほとんどの関数は、Context
を最初の引数として受け取らなくてはいけません。リクエスト固有でない関数は、
context.Background()
を使用することができます。しかし必要ないと思っていても、Context
を渡すようにしましょう。
Context
は渡すのがデフォルトです。
Context
を渡すのが間違いであることが明確な時だけcontext.Background()
を使うようにしましょう。構造体に
Context
メンバを追加しないでください。
代わりにContext
が必要な各メソッドにctxパラメータを追加してください。
署名が標準ライブラリやサードパーティ・ライブラリのインターフェイスに一致しなくてはいけないメソッドは例外です。カスタムContext型を作成したり、関数シグネチャでContext以外のインターフェイスを使用したりしないでください。
アプリケーションデータを渡したい場合は、パラメータ、レシーバ、グローバル変数に入れてください。
もし実際に存在するならば、Context
の値として持たせてください。
Contexts
は不変なので、同じctxを、同じ期限・キャンセルシグナル・資格情報・親トレースを持つ多数のcallに渡すことができます。つまり
Context
は渡すように。メソッドの引数として渡すように。
渡されるメソッドは、引数の最初がctxになるように設計する。【Copying】
予期せぬエイリアスを避ける為に、他のパッケージから構造体をコピーする時は気をつけてください。
例えば、bytes.Buffer
の構造体は[]byte
を含んでおります。
Bufferをコピーすると、コピーしたスライスは元の配列のエイリアスになってしまい、後続のメソッドで呼び出した際に驚くような結果になるかもしれません。一般的に、T型のメソッドが*Tのポインタ型に関連づいている場合、値をコピーしてはいけません。
【Crypto Rand】
鍵を生成する時は
math/rand
パッケージを使用してはいけません。使い捨ての1回限りであってもです。
seedを持たないジェネレーターは完全に予測可能です。
time.Nanoseconds()
のseedを持つものでも、ほんの少し予測ができます。
代わりに、crypto/rand
のリーダーを用いて、テキストが必要な場合は16進数かbase64を用いましょう。つまり
鍵生成は
crypto/rand
を用いる【Declaring Empty Slices】
空のスライスを宣言する時は
t := []string{}
ではなくvar t []string
を使いましょう。
var t []string
ではnilのスライス、t := []string{}
ではnilではない長さゼロのスライスになります。
どちらもlenもcapもゼロで、機能的には同じですが、nilスライスの方が望ましいです。JSONオブジェクトにエンコードする際など、nilではない長さゼロのスライスが好まれることがあることも覚えておきましょう。
([]string{}
は空配列にエンコードされますが、nilのスライスはnullにエンコードされます)インターフェースを設計する際、nilのスライスとnilではない長さ0のスライスに区別を設けることは避けてください。
微妙なプログラミングのエラーを引き起こす恐れがあります。Goにおけるnilのyより詳しい情報はこちらをみてください
つまり
空のスライスの宣言は
var t []string
を使う方が良い
nilと、nilでは無いが長さ0のスライスとを区別するようなインターフェースは設計しない【Doc Comments】
トップレベルのエクスポートされる自明でない型や関数名称にはdocコメントを書いてください。
コメントの規約についての詳細はこちらを参照してください。【Don't Panic】
こちらを参照してください。
標準のエラーハンドリングでpanicは使用せず、error
と複数の返り値を用いるようにしてください。【Error Strings】
エラー文言は他の文脈に続いて出力される為、頭文字や固有名詞で始まらない限り、大文字を用いたり、句点で終わるべきではありません。
つまりlog.Printf("Reading %s: %v", filename, err)
がメッセージの途中で不自然な大文字を使わずにフォーマットされるように、fmt.Errorf("Something bad")
ではなくfmt.Errorf("something bad")
でコーディングするようにしてください。
これは暗黙的なライン指向で他のメッセージ内に結合されないロギングには適用されません。つまり
単体で出力されるようなログを除いたエラー文言では、大文字始まりや句読点終わりのメッセージは使用しない
【Examples】
新しくパッケージを追加する際、実行できる、または完全なコールシーケンスを示す簡単なテストといった、使用例を含めてください。
詳しくはこちら【Goroutine Lifetimes】
goroutines
を使用する際は、ライフサイクルを明確にしてください。
goroutines
はチャンネルの送受信をブロックすることで「リーク」することがあります。
ブロックされたチャンネルが到達不可能であっても、ガベージコレクタはgoroutines
を終了させません。goroutineがリークしていない時でさえ、必要ないにもかかわらず放置していると些細で診断が困難な問題を引き起こす恐れがあります。
閉じたチャンネルでの送信はpanicになります。
使用中の入力を「結果が不要になった後」に編集すると、データの競合を起こし得ます。
goroutineを放置していると予期せぬメモリ使用量になる恐れがあります。並行コードはgoroutineのライフタイムが明らかになるように、シンプルに記載してください。
それが不可能な時は、いつ・どういった時にgoroutineが終了するのかをドキュメントに記しておいてください。つまり
goroutineはライフタイムを明確にする
それが無理な時は、いつ・どんな時に終了するのかドキュメントにまとめておく【Handle Errors】
こちらを参照ください。
errを_
で無視しないでください。
関数がエラーを返す場合、必ずチェックして関数が成功していることを確認して下さい。
エラーハンドルとしては、errを返却して下さい。または完全に例外的な状況であればpanic
を使用して下さい。つまり
エラーチェックはマスト
【Imports】
名前の衝突を避けるために、importをリネームするのは避けて下さい。
良いパッケージ名はリネームの必要がありません。import文は空行で区切られた塊で書いて下さい。
標準ライブラリはいつも最初の塊に記載して下さい。goimportsを用いると自動で実装してくれます。
つまり
importはリネームしないように
goimportsを使ってimport文が秩序だった記載になるようにしよう【Import Blank】
【Import Dot】
【In-Band Errors】
【Indent Error Flow】
【Initialisms】
【Interfaces】
Goのインターフェースは一般的に、インターフェースの値ではなく、型の値を用いるパッケージに属します。
実装するパッケージは、具体的な(ポインターや構造体の)型を返却するべきです。
そうすることによって、大規模なリファクタ無しで新しいメソッドを追加することができます。Mockのためのインターフェースを実装するべきではありません。
代わりに、実際に公開されているAPIを使用してテストできるように、APIを実装してください。使用される前にインターフェースを定義してはいけません。
現実的な使用例無しに、インターフェースの必要性を見極めるのは困難で、どんなメソッドが含まれている必要があるのかを見極めるのが困難であることは言わずもがなです。【Line Length】
Goコードには厳格な長さの規定はありませんが不快な長さは避けてください。
同様に、長い方が読みやすい際は、行を短くする為に改行を加えるような行為はやめてください。(ex.反復的な文章)関数の呼び出しや関数の宣言の途中で多かれ少なかれ例外はあるものの、人々が不自然に改行を行っている時、適度なパラメータ数で適度な長さの変数名であれば、改行は必要ないでしょう。
長い行は長い名称が使用されることが多く、その名称を取り除くことが助けになるでしょう。言い換えれば、基本的には、行の長さではなく書いている内容に基づいて改行を行ってください。
行が長すぎと感じたら、名称や意味を変更して、程よい長さにしてください。これは実は、関数の長さに対するアドバイスと同じです。
「関数はN行以内」といったルールはありませんが、確かに関数が長すぎる・小さすぎるといった問題はあり、その解決策は行数を数えることではなく境界を変更することです。つまり
Goでは1行の長さ、関数の行数に関して特に決まりはないけど、極端に長い・短いは避けましょう
意味が崩れないように長い変数名を使用しつつ、行が長くなってしまったら適宜変数名を変更する