Golang: 配列からスライスに変換する

TL;DR: slice := array[:]で変換できる Go言語にはリストの様なものが二つあります。配列(固定長)とスライス(可変長)です。 一般に、Go言語で配列を扱うことは多くないでしょう。 実際、多くのパッケージ(標準パッケージを含む)が要求するのはスライスです。 とは言っても一部のパッケージでは配列を取り扱っているものがあります。 例えば、crypto/sha512を見てみる と、以下の様な関数が存在します。 1 func Sum512(data []byte) [Size]byte ここで、Sizeは同パッケージ内で宣言されている定数で、値は64です。 つまり、この関数は64バイトの長さを持った配列を返します。 この関数は与えられたデータからSHA512チェックサムを計算するものです。 勿論、返ってきた値をそのまま使用することもあるとは思いますが、そのままの値は人間可読な値では無いため、hexdigestを得たいと思うでしょう。 Go言語にはもちろんのことながら、encoding/hexパッケージが存在し、簡単に16進文字列を得ることができます。 16進表記の文字列を得るためには、次の関数を使用します。 1 func EncodeToString(src []byte) string 引数に注目します。 要求されているのはbyteのスライスです。 Goでは、配列とスライスは基本的に別物ですから、以下の様に書くことはできません。 1 2 3 4 h := sha512.Sum512("foobar") // 型エラーが発生する EncodeToString(h) そうは言っても、配列とスライスは非常に似ています。 次のように書きたくなりますね。 1 2 h := sha512.Sum512("foobar") EncodeToString([]byte(h)) // 配列をスライスに変換したい しかし、次のようなエラーを生じます。 cannot convert sha512.Sum512("foobar") (type [64]byte) to type []byte 型変換はできないようです。 どうしたら良いのでしょうか。 Go言語では、配列の範囲インデックスを使った場合、返される値はスライスとなります。 1 2 a := [3]string{"foo", "bar", "baz"} s := a[0:2] // sはスライス また、インデックスを省略することもできます。 開始値を省略すれば、0を与えたものと見なされますし、終了値を省略すれば、配列の最後までを切り取ります。 1 2 3 a := [3]string{"foo", "bar", "baz"} s1 := a[:2] // a[0:2]と等しい s2 := a[0:] // a[0:3]と等しい では、両方省略するとどうなるでしょうか。 両方省略すると、もとの配列と同じ内容のスライスが返されます。 ...

2018-03-16 · nasa9084

テキストを列ごとにそろえて出力する

TL;DR: 標準パッケージtext/tabwriter を使用する コマンドラインツールで標準出力を良い感じにそろえて出力したい場合があります。 例えば、docker では、以下の様に出力されます。 1 2 3 4 $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8caad461b4a4 redis "docker-entrypoint.s…" 5 days ago Up 5 days 0.0.0.0:6379->6379/tcp redis-svr 329c9f9be035 mysql "docker-entrypoint.s…" 5 days ago Up 5 days 0.0.0.0:3306->3306/tcp mysql-svr このような map、あるいは構造体の配列 の様なものを、きれいに表形式の様に列をそろえて出力したい場合に便利なのが標準パッケージのtext/tabwriter です。 その名の通り、タブ区切りの文字列を良い感じに出力してくれるパッケージです。 text/tabwriterで定義されているのはいくつかの設定用定数 と、Writer構造体 のみです。 Writerは(勿論)io.Writerインターフェース を実装しています。 使用方法は通常のio.Writerとはすこし変わっていて、最初にWriter.Init() で初期化し、任意回数Writer.Write() で書き込みをした後、Writer.Flush() で整形した文字列を出力します。 NewWriter() 関数はWriter構造体をnew()した後Init()するのと同等です。 Writer.Init()関数及びNewWriter()関数に与える引数は以下の様になっています。 名前 型 内容 output io.Writer Flush()したときの出力先 minwidth int 1セルあたりの最小幅(パディングを含む) tabwidth int タブ文字の幅(スペースの個数と等しい) padding int パディング1 padchar byte パディング文字2 flags int 調整用フラグ 最後の引数である、調整用フラグには0(標準状態)を与えるか、パッケージ定数 の論理和を用いて設定を与えます。 ...

2018-01-29 · nasa9084

Application Specific Context

元ネタは@lestrrat さんの「Abusing type aliases to augment context.Context 」。 golangを用いてHTTPサーバを作る場合、ルーティングを定義するのに以下の様な関数を用います。 1 http.HandleFunc(path string, handler func(w http.ResponseWriter, r *http.Request) もちろん、http.Handleを用いる場合もありますし、gorilla/mux などのライブラリを用いることもあると思います。 ここで重要なのは、func(w http.ResponseWriter, r *http.Request)という引数の方です。 多くの場合、アプリケーションのハンドラ内ではデータベースなどの外部アプリケーション・ミドルウェアを用いることになります。 しかし、golangのHTTPアプリケーションでは、ハンドラ関数の形式がfunc (w http.ResponseWriter, r *http.Request)と決まっています。引数の追加はできないため、引数以外の方法でDB接続情報などを渡す必要があります。 これまで、golangでWebアプリケーション開発を行う場合によく用いられていたデータベースコネクションの保持方法は、dbパッケージを作成し、そこにパッケージ変数として持つ方法かと思います。が、グローバルな変数はできるだけ持ちたくない ですよね。 そこで、Go 1.8から追加されたcontext を使うことができます。http.Request にはcontext.Contextが入っていて、Request.Context() でget、Request.WithContext() でsetできます。 context.Contextに値を持たせる方法で最初に思いつくのはContext.WithValue() を用いる方法ですが、これは値を取得する度にtype assertionをする必要があり、あまりよくありません 。 これを解消するため、自分で型を定義するのがよいでしょう。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package context // internal context subpackage import ( "context" "errors" ) type withSomethingContext struct { context.Context something *Something } func WithSomething(ctx context.Context, something *Something) context.Context { return &withSomethingContext{ Context: ctx, something: something, } } func Something(ctx context.Context) (*Something, error) { if sctx, ok := ctx.(*withSomethingContext); ok { if sctx.something != nil { return sctx.something, nil } } return nil, errors.New(`no asscosiated something`) } このように定義をすることで、毎回type assertionをする必要もなくなり、すっきりします。 ...

2017-11-21 · nasa9084

sync.WaitGroup

Goroutineを使用して複数の処理を並列で実行、すべてが終わったら次の処理に進みたいという場合があると思います。 Goroutineでデータのリストを作るという処理を考えます。 データの順番は関係なく、すべてのGoroutineでのデータがそろったら次の処理をしたいという設定です。 この場合、単純に考えると以下のようなコードになりますが、以下のコードではデータがそろう前に次の処理が行われます。 1 2 3 4 5 6 7 datalist := []string{} for i := 0; i < 10; i++ { go func() { // something w/datalist } } fmt.Println("next step") このような場合に、sync.WaitGroupを使用します。 sync.WaitGroupは基本的にはただのカウンタですが、カウンタがゼロになるまで処理を待つことができます。 言葉で説明してもわかりにくいと思いますので、ソースコードを見てみましょう。 1 2 3 4 5 6 7 8 9 10 11 12 datalist := []string{} wg := sync.WaitGroup{} for i := 0; i < 10; i++ { wg.Add(1) // Goroutineの数だけカウンタを増やす go func() { // something w/datalist wg.Done() // カウンタを減らす } } wg.Wait() // カウンタが0になるまでブロックする fmt.Println("next step") 上記の様にすることで、for文の部分ではGoroutineで並列に実行しつつ、次の処理は並列実行部分が終わってからという動作をさせることができます。 ポイントはカウンタを増やす部分です。 Goroutine内ではなく、外側でAdd(1)します。 Goroutine内でAdd(1)してしまうと、wg.Wait()に到達した時点でGoroutineがまだどれも実行されておらず、次の処理に進んでしまう可能性があるので、必ずGoroutineの外側で実行することが肝要です。

2017-09-01 · nasa9084