Go言語でgorepっていう検索ツール書いた

ディレクトリ名やらファイル名やらGrep検索やらを一緒くたに正規表現で検索する"gorep"っていうツール書いた。

https://github.com/ryochack/gorep

以下のコマンドを打ち込むと、カレントディレクトリ以下から再帰的にgo..pにマッチするディレクトリ、ファイル、テキストファイルの文字列を表示する。

$ gorep -g go..p .

-gをつけるとgrep検索が有効になる。

gorep screenshot

Windowsで気軽に検索できるツールが欲しいってのがモチベーションだったけど、まだWindowsで動作検証していないっていう体たらく。

参考

Go言語の並行処理で総和を求める

パタヘネ本の7章(マルチコアとマルチプロセッサクラスタ)に以下のような(ちょっと違うけど)図が載っていたので、Goの並行処理を使って100,000個の数の和を求めてみた。

f:id:ryochack:20130421173532p:plain

最初の図とはちょっと違って、実際の制御はこんな感じ。

f:id:ryochack:20130424001717p:plain

値が2つ揃ったら、加算を行うgoroutineを生成する。 goroutineからは、channelを通して合計が送信される。 全ての数を足し切るまでこれを繰り返す。(足す順番は気にしない)

足す数は0〜99,999までの等差数列。 time.Sleep(1 * time.Millisecond)は、後々の単純総和コードとの比較のために、擬似的に処理を重くしているつもり。(足し算だけだと処理が軽すぎて並行処理にした方が遅かった…)

下は単純に総和を求めるコード。

実行結果

$ time go run sum_goroutine.go
704982704
go run sum_goroutine.go  0.52s user 1.47s system 119% cpu 1.668 total

$ time go run sum_simple.go
704982704
go run sum_simple.go  1.23s user 1.75s system 2% cpu 2:07.93 total

単純な総和が2分強かかっているのに対して、goroutineを使うと1.668秒。

本当は、子ルーチン(生成したgoroutine)同士で通信を行なって、親ルーチン(大本のgoroutine)には結果だけを返すように実装したかったけど、channel制御が複雑になってしまって断念。

子ルーチンの計算結果を親ルーチンに返して、それを元にまた子ルーチンを生成する方法で実装した。

channel制御むずかしい…。すぐにdeadlock起こす。

チャネルの配列(スライス)を生成する

Go言語でチャネルの配列(スライス)を生成するには、以下のように宣言する。

チャネルの固定長配列

var chans = []chan int {
    make(chan int),
    make(chan int),
    make(chan int),
    make(chan int),
}

もしくは

var chans [4]chan int
for i := range chans {
   chans[i] = make(chan int)
}

チャネルの動的配列

変数numの大きさのチャネル配列確保。

num := 10
ch := make([]chan int, num)
for i, _ := range ch {
    ch[i] = make(chan int)
}

2013.4.21 追記
これは配列で、

var chans [4]chan int

こっちはスライスですね。

var chans = []chan int
ch := make([]chan int, num)

参考

Go言語のflagパッケージを使う

Go言語でコマンドラインオプションを扱う - そこはかとなく書くよん。

に触発されたので、flagパッケージについて自分で使ってみてハマったところを書いておく。

コマンドラインオプションの略称を登録する

flag.IntVar()などのXxxVar()を使う。

これで -boolでも-bでも同じ変数にフラグの結果が反映されるようになる。

$ go run flag_shorthand.go -bool -int 100 -string hoge
bool flag   = true
int flag    = 100
string flag = hoge

$ go run flag_shorthand.go -b -i 100 -s hoge
bool flag   = true
int flag    = 100
string flag = hoge

コマンドラインオプションを最後まで解析する

flag.Parse()の解析は、非フラグ引数(頭に"-"の付いた引数)の前、もしくは"--"の後で打ち切られる。

Flag parsing stops just before the first non-flag argument ("-" is a non-flag argument) or after the terminator "--".

http://golang.org/pkg/flag/#pkg-overview

そのため、以下のようなコマンドラインだとxxxの部分でフラグ解析が打ち切られるため、フラグが設定されない。

$ go run flag_shorthand.go xxx -b -i 100 -s hoge
bool flag   = false
int flag    = 0
string flag = blank

そこで、flag.NewFlagSet()を用いて以下のような処理を書くことでフラグ引数の前にxxxのような非フラグ引数があっても最後までフラグ解析を行わせることができる。

flag.NewFlagSet()でFlagSetを生成した場合、Parse()には解析する文字列のスライスが引数として要求される。その引数の開始インデックスを1ずつ進めてやればいい。

$ go run flag_checkall.go xxx -b -i 100 -s hoge
bool flag   = true
int flag    = 100
string flag = hoge

外部パッケージでoptargなるものがあるそうなので、後で試してみよう。

Go言語は定数配列宣言ができない

以下のコードはコンパイルが通らない。

package main

func main() {
    const array = [...]int {1,2,3,4,5}
}

コンパイルエラー。

const initializer must be constan

Goでは、配列やスライスは全てランタイム中に生成される。 それに対して、定数はコンパイル時に生成される。 そのため、配列を定数として宣言することはできないらしい。

参考