20200603のGoに関する記事は5件です。

プログラミング言語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など、実行する前にバグがわかるのは助かる。実行時に安全。

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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")
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

| <- この演算子は何者?

はじめに

最近Goのlogパッケージの記事を読んでいたらパイプライン(| <- これ)が+のように中間演算子として使われているのを見て「なにその演算子?」となったので調べて記事にしました。
基本的に記事のコードはGoです。

問題のコード

log.SetFlags(log.LstdFlags | log.Lshortfile)

ちなみにlog.LstdFlagslog.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: 01100110

Example

少しの説明だけでは分かりづらいかな、と思ったのでPlaygroundにサンプルコードを書きました。
説明分かりづらいわ!って人はこっちを見た方が理解が早いかもしれません。
https://play.golang.org/p/wq8Dr7_SpMN

logパッケージを読んで理解を深める

このチャプターでは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.LstdFlagslog.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から始まるのでLdate1 << 0Ltime1 << 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 {...

ここで評価している式について詳しく見ます。
この式はLdateLtimeLmicrosecondsフラグが与えられているか判定しています。

l.flagにはLstdFlags | Lshortfileが入っているのでbitで表すと00010011となり、
Ldate|Ltime|Lmicrosecondsを上の表を見て計算すると00000111となります。
よってbitにすると00010011 & 00000111の形になります。

&を計算すると l.flag00000011(十進数に直すと3)になり、この分岐を進むことになります。

二番目の分岐

if l.flag&LUTC != 0 {...

二番目の分岐はLUTCフラグが与えられているかを判定しています。

l.flag00010011LUTCは上の表から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パッケージのソースコード

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

golang dep にはまった

dep ensure -v
で数時間かかっても終わらないのでおかしいなと思ったら、
githubに登録したrsakeyにパスをつけていたことが原因だったっぽく、パスなしkeyを登録し直したら、1分で終わった。
ssh-agentも紹介してもらったけど、それを使えば大丈夫なのかは未確認。今度やってみよう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む