- 投稿日:2020-06-03T20:41:57+09:00
プログラミング言語Go完全入門 質問会 質疑応答メモ(ラフ)
Q.
interface の使い方、継承との違いがまだしっくりときていません。できること、できないことをできれば他の言語(Java, Rubyなど)と比較して解説していただけると嬉しいです。
A.
Goに継承はない。
interfaceは抽象化したい時に使う。
httphandlerが有名なinterfaceのひとつ。あるいは、io.Reader/Writer。
Javaはimplemetsする必要があるが、Goでは必要ない。
⇒ダックタイピング
Goではinterfaceを後から作ることができる。Q.
保守運用が楽になるエラーハンドリングの方法や参考になる資料があれば教えていただきたいです。根本原因にたどり着くまでに時間がかかることがよくあります。
A.
ログを出しましょう。
ラップして上に投げればいいわけではない。
エラーを返すと受取り側の処理が複雑になるので、都度panicで出したい。Q.
初心者向けに、コードリーディングに最適なライブラリなどありましたら教えてほしいです。
A.
標準ライブラリ、という回答が一般的。
stringsパッケージが簡単かも。
デバッカーでライブラリの中まで入れると良い。
身近なライブラリを読むといいかも。
ライブラリに書いてあることが正しいわけではない。デバッカーdelve
Golangのデバッガdelveの使い方Q.
業務でGoを使わない場合、基礎を学んだ後はどのように学習していくのがいいですか?
A.
自分が使うものを作るのが一番。
日々の生活に困っているなど。。Q.
jsonを生成するのがとても辛いのですが、ベストプラクティスってあるのでしょうか?
A.
隠したいフィールドは出して。出したいフィールドは出す。
jsonの辛みを分析して、そのライブラリを作るといいかも。
jsonパッケージのライブラリを読んでみるとか。ちなみに、structは入れ子にできる。
Q.
RubyからGoに来たエンジニアとJavaからGoに来たエンジニアでテストや設計で意見が割れることがあるので、Goならではの思想、指針みたいなものがあれば聞きたい。
A.
Goの思想はシンプル。
シンプルなものはどれかで選ぶと良い。
シンプルというのは「複雑ではない」ということ。
複雑に作ると、後々保守性が悪くなったり、依存度が高くなる。Q.
ORMの良さ、どのORMがいいのか、何をするためのものかを教えて下さい。
A.
自分でSQL書くことが多い。Q.
goに関する書籍でおすすめの本、最近読んでよかったものなどありましたら聞きたいです
A.
- プログラミング言語Go
- Goによる並行処理
- Goならわかるシステムプログラミング
- Goの公式ブログなどもオススメ
Q.
Goの将来性について、ご自身の考えで構いませんのでお伺いしたいです。Goのシェアはみるみる伸びていますが、Goを導入したけどPythonやRubyに戻したという話もちらほら聞くので、気になりました。
A.
Docker, k8sなどクラウド界隈ではしばらく使われるだろう。
Ruby, Pythonなど言語だけに注目して議論するのは危険。チームの状況諸々を考慮する必要がある。早期にコンパイルNGなど、実行する前にバグがわかるのは助かる。実行時に安全。
参考リンク
- 投稿日:2020-06-03T20:23:23+09:00
【Golang】ゴルーチン⑤複数goroutinからチャネルに値を入れ取り出す
【Golang】ゴルーチン⑤複数goroutinからチャネルに値を入れ取り出す
Golangの基礎学習〜Webアプリケーション作成までの学習を終えたので、復習を兼ねてまとめていく。 基礎〜応用まで。
package main //producerとconsumer //複数goroutinからチャネルに値を入れ取り出す //mainから、いくつかのProducerを走らせて、 //その処理結果をchannelに入れて、Consumerでchanelの値を集めれて //処理を行う import ( "fmt" "sync" "time" ) func producer(ch chan int, i int) { ch <- i * 5 } func consumer(ch chan int, wg *sync.WaitGroup) { for i := range ch { //インナー無名関数 //処理をまとめたいとき。 func() { defer wg.Done() fmt.Println("process", i*100) }() //() 実行 } fmt.Println("全producer受け取り終了") } func main() { //並列処理 全てのGoroutinが終わるまで待つ var wg sync.WaitGroup //channelを作成 ch := make(chan int) // Producer //10回分作成 for i := 0; i < 10; i++ { //Consumerが全て受け取ったかを確認するため //10回追加 wg.Add(1) go producer(ch, i) } // Consumer //同じwg なのでアドレスで渡す go consumer(ch, &wg) wg.Wait() //ここでクローズするのは、まだconsumerのgoroutinが走っている為 //consumerだけ走ったまま終了してしまう //Consumer内のchannelが全て出力されるのを待つ close(ch) //Consumerが全て終わったら time.Sleep(2 * time.Second) fmt.Println("Done") }
- 投稿日:2020-06-03T15:03:46+09:00
Goの配列とスライスについて
書き方
配列
list := [3]string{"a","b","c"}
配列 := [長さの数字]型{要素}
list := […]string{"a","b","c"}
要素の数を自動カウントスライス
slice := []string{"a","b","c"}
スライス := [長さは指定しないので何も書かない]型{要素}
slice := list[0:5]
スライス := 配列[n番目:m-1番目]
※部分列の取り方→配列[start:end]or[start:]or[:end]or[:]
slice := make([]int,5,5)
make([ ]型,長さ,容量)性質
スライスの長さlen(slice)と容量cap(slice)
- 長さ=要素の数
- 容量=スライスの最初の要素から数えて、元となる配列の要素
※配列をスライスしても元の要素の情報を参照型で保っているインデックス(順番)
- 配列もスライスもインデックスは0から数える
例)list := [3]string{"0番目","1番目","2番目"}
要素の追加
- 配列は宣言時に要素の数が固定
- スライスは組み込み関数append()を使う
例) newSlice = append(slice, 追加要素) →新しいスライスに元のスライス+最後に要素が追加される- appendが容量を超える場合、別途、容量を2倍した新しい配列をコピーします。コピーは処理コストが大きいのでmakeを使うのがおすすめ
要素の変更、代入
- スライスの要素を指定することでそこだけを変更できる
package main import ( "fmt" "io/ioutil" "log" "flag" ) func main() { slice := [] int{1,2,3,4} slice[0] = 9 fmt.Println(slice)→[9,2,3,4]
- スライスの型が一致している場合、スライスを代入することができる (元の配列の情報も引き継がれる)
ゼロ値
- スライスのゼロ値はnilでlen=0,cap=0
- 投稿日:2020-06-03T14:27:08+09:00
| <- この演算子は何者?
はじめに
最近Goのlogパッケージの記事を読んでいたらパイプライン(| <- これ)が+のように中間演算子として使われているのを見て「なにその演算子?」となったので調べて記事にしました。
基本的に記事のコードはGoです。問題のコード
log.SetFlags(log.LstdFlags | log.Lshortfile)ちなみに
log.LstdFlags
とlog.Lshortfile
はどちらもint型です(具体的な実装はあとにあります)。公式ドキュメントにはどう書かれていたか
Goの公式言語仕様には + や - などと一緒に演算子の一つとして載っています。
+ sum integers, floats, complex values, strings - difference integers, floats, complex values * product integers, floats, complex values / quotient integers, floats, complex values % remainder integers & bitwise AND integers | bitwise OR integers ^ bitwise XOR integers &^ bit clear (AND NOT) integers << left shift integer << unsigned integer >> right shift integer >> unsigned integerですがこのドキュメントでは | は + や - と同様に、そんなん説明しなくても知ってるやろみたいな感じだったので結局この演算子が何をするものなのか分かりませんでした。その代わりbitwase ORという演算子の名前?的なものをゲットすることが出来ました。
そしてGoogleで"bitwise OR"とかなんとか検索するとbitwise ORというのはbit演算子の1つだということが分かりました。
bit演算子とは
bit -> bit演算 -> bit演算子という順に調べて分かった事を軽く説明していきます。
そもそもbitって何?
みなさんはご存知だと思いますがコンピュータは小さなon/offスイッチの集合で出来ています。
このスイッチのことをbitといい、それを8個ずつグループにしたものをbyteといいます。
bitだけではon offの2種類の数字 == 0と1しか表現出来ませんが、byteでは8桁の二進数として表現できます。これによってコンピュータは大きな数字の計算ができるわけですね。
00000000
のように表記し、右端から0bit目、1bit目と数えていきます。bit演算って何をするの?
スイッチのon/offを制御するのがbit演算です。
基本的に8の倍数個をまとめて、つまりbyte単位で処理を行います。
bit演算はbit演算子を使って行います。bit演算子ってどんなものがあるの?
ここでは後の"logパッケージを読んで理解を深める"チャプターで使う4つの演算子について説明します。
| (bitwise OR)
この記事のメインの演算子である | (bitwise OR)は二つのスイッチを比較してどちらかが1であれば1、どちらも0であれば0を返します。
e.g.
c = a | b a: 01010101 b: 11001100 c: 11011101& (bitwise AND)
bitwise ORの対の演算子である & (bitwise AND)は二つのスイッチを比較してどちらも1であれば1、そうでなければ0を返します。
e.g.
c = a & b a: 01010101 b: 11001100 c: 01000100<< (left shift) と >> (right shift)
<< (left shift)はスイッチのon/offの状態を一つ左に、>> (right shift)は一つ右に動かします。
ずらして空いた枠には0が入り、決められた桁数を超えた場合にははみ出た部分が消えます。消える事をオーバーフローといいます。e.g.
c = a << 1 a: 10101010 c: 01010100 --- c = b >> 1 b: 11001100 c: 01100110Example
少しの説明だけでは分かりづらいかな、と思ったのでPlaygroundにサンプルコードを書きました。
説明分かりづらいわ!って人はこっちを見た方が理解が早いかもしれません。
https://play.golang.org/p/wq8Dr7_SpMNlogパッケージを読んで理解を深める
このチャプターではGoの標準パッケージであるlogパッケージを読んでbit演算子の具体的な使い方を学びます。
Go言語ではbit演算子はint型の数字に対して使う演算子であり、とりあえずこの記事ではbit演算子を数字を二進数(bitの集合)として扱うための演算子として認識しています(分かりやすくするため&筆者の知識不足のため)。
今回は下記のコードを実行したとして読み進めていきます。
package main import "log" func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) log.Println("hoge") log.SetFlags(log.LstdFlags | log.Lmicroseconds) log.Println("fuga") }logパッケージではbit演算を使ってフラグの管理を行っています。
では実際のコードを見ていきましょう。まず
log.SetFlags
で引数に渡しているlog.LstdFlags
、log.Lshortfile
などのフラグの定義から見ていきます。フラグの定義
const ( Ldate = 1 << iota // the date in the local time zone: 2009/01/23 | Ltime // the time in the local time zone: 01:23:23 | Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. | Llongfile // full file name and line number: /a/b/c/d.go:23 | Lshortfile // final file name element and line number: d.go:23. overrides Llongfile | LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone | Lmsgprefix // move the "prefix" from the beginning of the line to before the message | LstdFlags = Ldate | Ltime // initial values for the standard logger | )これがフラグの定義です。
iota
というのはconst識別子で連番の整数を生成します。iotaは0から始まるのでLdate
は1 << 0
、Ltime
は1 << 1
のように続いていきます。1は二進数で表すと
00000001
なのでそこからどんどんleft shiftでずらしていくとこんな感じになります。
定数名 値 Ldate 00000001 Ltime 00000010 Lmicroseconds 00000100 Llongfile 00001000 Lshortfile 00010000 LUTC 00100000 Lmsgprefix 01000000 LstdFlags 00000011 フラグの値を把握できたら、次はそれがどのように
log.Println
に適応されるのかを見ていきましょう。フラグの適応
全ての分岐を読むのは面倒なので実装の一部を切り取って説明したいと思います。
ここではhoge
の方を評価していると思って読んでください。if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { if l.flag&LUTC != 0 { t = t.UTC() } if l.flag&Ldate != 0 { year, month, day := t.Date() itoa(buf, year, 4) *buf = append(*buf, '/') itoa(buf, int(month), 2) *buf = append(*buf, '/') itoa(buf, day, 2) *buf = append(*buf, ' ') } if l.flag&(Ltime|Lmicroseconds) != 0 { hour, min, sec := t.Clock() itoa(buf, hour, 2) *buf = append(*buf, ':') itoa(buf, min, 2) *buf = append(*buf, ':') itoa(buf, sec, 2) if l.flag&Lmicroseconds != 0 { *buf = append(*buf, '.') itoa(buf, t.Nanosecond()/1e3, 6) } *buf = append(*buf, ' ') } }
Ldate
,Ltime
,Lmicroseconds
フラグを評価しているif文です。
l.flag
というフィールドにlog.SetFlags
でセットしたフラグが入ってます。では最初の分岐を見ていきましょう。
if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {...ここで評価している式について詳しく見ます。
この式はLdate
、Ltime
、Lmicroseconds
フラグが与えられているか判定しています。
l.flag
にはLstdFlags | Lshortfile
が入っているのでbitで表すと00010011
となり、
Ldate|Ltime|Lmicroseconds
を上の表を見て計算すると00000111
となります。
よってbitにすると00010011 & 00000111
の形になります。&を計算すると
l.flag
は00000011
(十進数に直すと3)になり、この分岐を進むことになります。二番目の分岐
if l.flag&LUTC != 0 {...二番目の分岐は
LUTC
フラグが与えられているかを判定しています。
l.flag
は00010011
、LUTC
は上の表から00100000
なので&を計算すると00000000
が返ってきます。
00000000
は十進数に直しても0なのでこの分岐は進みません。
セットしていないフラグの評価はしっかり防げていますね。三番目以降の分岐は二番目以前と大体同じなので割愛しますが、その後もしっかりセットしたフラグは評価され、セットしていないフラグは評価されないようになっています。
このチャプターの最初のコードを実行してみるとこのようになります。
2009/11/10 23:00:00 prog.go:7: hoge 2009/11/10 23:00:00.000000 fuga
hoge
の方ではprog.go:7
のようにファイルのどこで実行したかと日時が確認でき、Lshortfile
と日時を確認できるLstdFlags
が評価されていることが
fuga
の方では時刻の表記が23:00:00.000000
とmsまで表示されるようになっており、Lmicroseconds
が評価されていることが分かりますね。まとめ
今回はGoのコードで説明しましたがbit演算はGo以外のほとんど言語で実行できます。
フラグ管理にぜひbit演算を活用してみてはいかがでしょうか?この記事では具体的な使い道としてフラグ管理を取り上げましたが、他にもたくさん使い道があるようなので気になる方はこの記事を読んでみるといいと思います!
ビット演算 (bit 演算) の使い方を総特集! 〜 マスクビットから bit DP まで 〜 - Qiita
あとこの記事の中でなにか間違っていることが書いてあったら遠慮せずにコメントしたり修正リクエストを送っていただきたいです。よろしくお願いします!
参考
Go Tips連載8: logパッケージでログ出力している場所の情報を出す | フューチャー技術ブログ
The Go Programming Language Specification - The Go Programming Language
ビット演算入門 - Qiita
logパッケージのソースコード
- 投稿日:2020-06-03T08:11:31+09:00
golang dep にはまった
dep ensure -v
で数時間かかっても終わらないのでおかしいなと思ったら、
githubに登録したrsakeyにパスをつけていたことが原因だったっぽく、パスなしkeyを登録し直したら、1分で終わった。
ssh-agentも紹介してもらったけど、それを使えば大丈夫なのかは未確認。今度やってみよう。